src/haptic/linux/SDL_syshaptic.c
author Sam Lantinga
Mon, 04 May 2020 13:17:43 -0700
changeset 13789 a359f4f93439
parent 13645 f7fc52b64177
child 13864 3aa34ecfeb63
permissions -rw-r--r--
Improvement for bug 3446 - The haptic API does not allow to select the direction axes

meyraud705

I see how the documentation is confusing. I think that the choice of the axis is an implementation detail. The documentation should state the goal of this value, so I propose this wording:

"Use this value to play an effect on the steering wheel axis. This provides
better compatibility across platforms and devices as SDL will guess the
correct axis."

Value could even be renamed 'SDL_HAPTIC_STEERING_AXIS'.

For Linux, sending an effect on the X axis with a Logitech wheel works. Others brands don't have driver for Linux as far as I know.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #ifdef SDL_HAPTIC_LINUX
    24 
    25 #include "SDL_assert.h"
    26 #include "SDL_haptic.h"
    27 #include "../SDL_syshaptic.h"
    28 #include "SDL_joystick.h"
    29 #include "../../joystick/SDL_sysjoystick.h"     /* For the real SDL_Joystick */
    30 #include "../../joystick/linux/SDL_sysjoystick_c.h"     /* For joystick hwdata */
    31 #include "../../core/linux/SDL_udev.h"
    32 
    33 #include <unistd.h>             /* close */
    34 #include <linux/input.h>        /* Force feedback linux stuff. */
    35 #include <fcntl.h>              /* O_RDWR */
    36 #include <limits.h>             /* INT_MAX */
    37 #include <errno.h>              /* errno, strerror */
    38 #include <math.h>               /* atan2 */
    39 #include <sys/stat.h>           /* stat */
    40 
    41 /* Just in case. */
    42 #ifndef M_PI
    43 #  define M_PI     3.14159265358979323846
    44 #endif
    45 
    46 
    47 #define MAX_HAPTICS  32         /* It's doubtful someone has more then 32 evdev */
    48 
    49 static int MaybeAddDevice(const char *path);
    50 #if SDL_USE_LIBUDEV
    51 static int MaybeRemoveDevice(const char *path);
    52 static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath);
    53 #endif /* SDL_USE_LIBUDEV */
    54 
    55 /*
    56  * List of available haptic devices.
    57  */
    58 typedef struct SDL_hapticlist_item
    59 {
    60     char *fname;                /* Dev path name (like /dev/input/event1) */
    61     SDL_Haptic *haptic;         /* Associated haptic. */
    62     dev_t dev_num;
    63     struct SDL_hapticlist_item *next;
    64 } SDL_hapticlist_item;
    65 
    66 
    67 /*
    68  * Haptic system hardware data.
    69  */
    70 struct haptic_hwdata
    71 {
    72     int fd;                     /* File descriptor of the device. */
    73     char *fname;                /* Points to the name in SDL_hapticlist. */
    74 };
    75 
    76 
    77 /*
    78  * Haptic system effect data.
    79  */
    80 struct haptic_hweffect
    81 {
    82     struct ff_effect effect;    /* The linux kernel effect structure. */
    83 };
    84 
    85 static SDL_hapticlist_item *SDL_hapticlist = NULL;
    86 static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
    87 static int numhaptics = 0;
    88 
    89 #define test_bit(nr, addr) \
    90    (((1UL << ((nr) & 31)) & (((const unsigned int *) addr)[(nr) >> 5])) != 0)
    91 #define EV_TEST(ev,f) \
    92    if (test_bit((ev), features)) ret |= (f);
    93 /*
    94  * Test whether a device has haptic properties.
    95  * Returns available properties or 0 if there are none.
    96  */
    97 static int
    98 EV_IsHaptic(int fd)
    99 {
   100     unsigned int ret;
   101     unsigned long features[1 + FF_MAX / sizeof(unsigned long)];
   102 
   103     /* Ask device for what it has. */
   104     ret = 0;
   105     if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(features)), features) < 0) {
   106         return SDL_SetError("Haptic: Unable to get device's features: %s",
   107                             strerror(errno));
   108     }
   109 
   110     /* Convert supported features to SDL_HAPTIC platform-neutral features. */
   111     EV_TEST(FF_CONSTANT, SDL_HAPTIC_CONSTANT);
   112     EV_TEST(FF_SINE, SDL_HAPTIC_SINE);
   113     /* !!! FIXME: put this back when we have more bits in 2.1 */
   114     /* EV_TEST(FF_SQUARE, SDL_HAPTIC_SQUARE); */
   115     EV_TEST(FF_TRIANGLE, SDL_HAPTIC_TRIANGLE);
   116     EV_TEST(FF_SAW_UP, SDL_HAPTIC_SAWTOOTHUP);
   117     EV_TEST(FF_SAW_DOWN, SDL_HAPTIC_SAWTOOTHDOWN);
   118     EV_TEST(FF_RAMP, SDL_HAPTIC_RAMP);
   119     EV_TEST(FF_SPRING, SDL_HAPTIC_SPRING);
   120     EV_TEST(FF_FRICTION, SDL_HAPTIC_FRICTION);
   121     EV_TEST(FF_DAMPER, SDL_HAPTIC_DAMPER);
   122     EV_TEST(FF_INERTIA, SDL_HAPTIC_INERTIA);
   123     EV_TEST(FF_CUSTOM, SDL_HAPTIC_CUSTOM);
   124     EV_TEST(FF_GAIN, SDL_HAPTIC_GAIN);
   125     EV_TEST(FF_AUTOCENTER, SDL_HAPTIC_AUTOCENTER);
   126     EV_TEST(FF_RUMBLE, SDL_HAPTIC_LEFTRIGHT);
   127 
   128     /* Return what it supports. */
   129     return ret;
   130 }
   131 
   132 
   133 /*
   134  * Tests whether a device is a mouse or not.
   135  */
   136 static int
   137 EV_IsMouse(int fd)
   138 {
   139     unsigned long argp[40];
   140 
   141     /* Ask for supported features. */
   142     if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(argp)), argp) < 0) {
   143         return -1;
   144     }
   145 
   146     /* Currently we only test for BTN_MOUSE which can give fake positives. */
   147     if (test_bit(BTN_MOUSE, argp) != 0) {
   148         return 1;
   149     }
   150 
   151     return 0;
   152 }
   153 
   154 /*
   155  * Initializes the haptic subsystem by finding available devices.
   156  */
   157 int
   158 SDL_SYS_HapticInit(void)
   159 {
   160     const char joydev_pattern[] = "/dev/input/event%d";
   161     char path[PATH_MAX];
   162     int i, j;
   163 
   164     /*
   165      * Limit amount of checks to MAX_HAPTICS since we may or may not have
   166      * permission to some or all devices.
   167      */
   168     i = 0;
   169     for (j = 0; j < MAX_HAPTICS; ++j) {
   170 
   171         snprintf(path, PATH_MAX, joydev_pattern, i++);
   172         MaybeAddDevice(path);
   173     }
   174 
   175 #if SDL_USE_LIBUDEV
   176     if (SDL_UDEV_Init() < 0) {
   177         return SDL_SetError("Could not initialize UDEV");
   178     }
   179 
   180     if ( SDL_UDEV_AddCallback(haptic_udev_callback) < 0) {
   181         SDL_UDEV_Quit();
   182         return SDL_SetError("Could not setup haptic <-> udev callback");
   183     }
   184 
   185     /* Force a scan to build the initial device list */
   186     SDL_UDEV_Scan();
   187 #endif /* SDL_USE_LIBUDEV */
   188 
   189     return numhaptics;
   190 }
   191 
   192 int
   193 SDL_SYS_NumHaptics(void)
   194 {
   195     return numhaptics;
   196 }
   197 
   198 static SDL_hapticlist_item *
   199 HapticByDevIndex(int device_index)
   200 {
   201     SDL_hapticlist_item *item = SDL_hapticlist;
   202 
   203     if ((device_index < 0) || (device_index >= numhaptics)) {
   204         return NULL;
   205     }
   206 
   207     while (device_index > 0) {
   208         SDL_assert(item != NULL);
   209         --device_index;
   210         item = item->next;
   211     }
   212 
   213     return item;
   214 }
   215 
   216 #if SDL_USE_LIBUDEV
   217 static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)
   218 {
   219     if (devpath == NULL || !(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) {
   220         return;
   221     }
   222 
   223     switch( udev_type )
   224     {
   225         case SDL_UDEV_DEVICEADDED:
   226             MaybeAddDevice(devpath);
   227             break;
   228 
   229         case SDL_UDEV_DEVICEREMOVED:
   230             MaybeRemoveDevice(devpath);
   231             break;
   232 
   233         default:
   234             break;
   235     }
   236 
   237 }
   238 #endif /* SDL_USE_LIBUDEV */
   239 
   240 static int
   241 MaybeAddDevice(const char *path)
   242 {
   243     struct stat sb;
   244     int fd;
   245     int success;
   246     SDL_hapticlist_item *item;
   247 
   248     if (path == NULL) {
   249         return -1;
   250     }
   251 
   252     /* check to see if file exists */
   253     if (stat(path, &sb) != 0) {
   254         return -1;
   255     }
   256 
   257     /* check for duplicates */
   258     for (item = SDL_hapticlist; item != NULL; item = item->next) {
   259         if (item->dev_num == sb.st_rdev) {
   260             return -1;  /* duplicate. */
   261         }
   262     }
   263 
   264     /* try to open */
   265     fd = open(path, O_RDWR, 0);
   266     if (fd < 0) {
   267         return -1;
   268     }
   269 
   270 #ifdef DEBUG_INPUT_EVENTS
   271     printf("Checking %s\n", path);
   272 #endif
   273 
   274     /* see if it works */
   275     success = EV_IsHaptic(fd);
   276     close(fd);
   277     if (success <= 0) {
   278         return -1;
   279     }
   280 
   281     item = (SDL_hapticlist_item *) SDL_calloc(1, sizeof (SDL_hapticlist_item));
   282     if (item == NULL) {
   283         return -1;
   284     }
   285 
   286     item->fname = SDL_strdup(path);
   287     if (item->fname == NULL) {
   288         SDL_free(item);
   289         return -1;
   290     }
   291 
   292     item->dev_num = sb.st_rdev;
   293 
   294     /* TODO: should we add instance IDs? */
   295     if (SDL_hapticlist_tail == NULL) {
   296         SDL_hapticlist = SDL_hapticlist_tail = item;
   297     } else {
   298         SDL_hapticlist_tail->next = item;
   299         SDL_hapticlist_tail = item;
   300     }
   301 
   302     ++numhaptics;
   303 
   304     /* !!! TODO: Send a haptic add event? */
   305 
   306     return numhaptics;
   307 }
   308 
   309 #if SDL_USE_LIBUDEV
   310 static int
   311 MaybeRemoveDevice(const char* path)
   312 {
   313     SDL_hapticlist_item *item;
   314     SDL_hapticlist_item *prev = NULL;
   315 
   316     if (path == NULL) {
   317         return -1;
   318     }
   319 
   320     for (item = SDL_hapticlist; item != NULL; item = item->next) {
   321         /* found it, remove it. */
   322         if (SDL_strcmp(path, item->fname) == 0) {
   323             const int retval = item->haptic ? item->haptic->index : -1;
   324 
   325             if (prev != NULL) {
   326                 prev->next = item->next;
   327             } else {
   328                 SDL_assert(SDL_hapticlist == item);
   329                 SDL_hapticlist = item->next;
   330             }
   331             if (item == SDL_hapticlist_tail) {
   332                 SDL_hapticlist_tail = prev;
   333             }
   334 
   335             /* Need to decrement the haptic count */
   336             --numhaptics;
   337             /* !!! TODO: Send a haptic remove event? */
   338 
   339             SDL_free(item->fname);
   340             SDL_free(item);
   341             return retval;
   342         }
   343         prev = item;
   344     }
   345 
   346     return -1;
   347 }
   348 #endif /* SDL_USE_LIBUDEV */
   349 
   350 /*
   351  * Gets the name from a file descriptor.
   352  */
   353 static const char *
   354 SDL_SYS_HapticNameFromFD(int fd)
   355 {
   356     static char namebuf[128];
   357 
   358     /* We use the evdev name ioctl. */
   359     if (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) <= 0) {
   360         return NULL;
   361     }
   362 
   363     return namebuf;
   364 }
   365 
   366 
   367 /*
   368  * Return the name of a haptic device, does not need to be opened.
   369  */
   370 const char *
   371 SDL_SYS_HapticName(int index)
   372 {
   373     SDL_hapticlist_item *item;
   374     int fd;
   375     const char *name;
   376 
   377     item = HapticByDevIndex(index);
   378     /* Open the haptic device. */
   379     name = NULL;
   380     fd = open(item->fname, O_RDONLY, 0);
   381 
   382     if (fd >= 0) {
   383 
   384         name = SDL_SYS_HapticNameFromFD(fd);
   385         if (name == NULL) {
   386             /* No name found, return device character device */
   387             name = item->fname;
   388         }
   389         close(fd);
   390     }
   391 
   392     return name;
   393 }
   394 
   395 
   396 /*
   397  * Opens the haptic device from the file descriptor.
   398  */
   399 static int
   400 SDL_SYS_HapticOpenFromFD(SDL_Haptic * haptic, int fd)
   401 {
   402     /* Allocate the hwdata */
   403     haptic->hwdata = (struct haptic_hwdata *)
   404         SDL_malloc(sizeof(*haptic->hwdata));
   405     if (haptic->hwdata == NULL) {
   406         SDL_OutOfMemory();
   407         goto open_err;
   408     }
   409     SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
   410 
   411     /* Set the data. */
   412     haptic->hwdata->fd = fd;
   413     haptic->supported = EV_IsHaptic(fd);
   414     haptic->naxes = 2;          /* Hardcoded for now, not sure if it's possible to find out. */
   415 
   416     /* Set the effects */
   417     if (ioctl(fd, EVIOCGEFFECTS, &haptic->neffects) < 0) {
   418         SDL_SetError("Haptic: Unable to query device memory: %s",
   419                      strerror(errno));
   420         goto open_err;
   421     }
   422     haptic->nplaying = haptic->neffects;        /* Linux makes no distinction. */
   423     haptic->effects = (struct haptic_effect *)
   424         SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
   425     if (haptic->effects == NULL) {
   426         SDL_OutOfMemory();
   427         goto open_err;
   428     }
   429     /* Clear the memory */
   430     SDL_memset(haptic->effects, 0,
   431                sizeof(struct haptic_effect) * haptic->neffects);
   432 
   433     return 0;
   434 
   435     /* Error handling */
   436   open_err:
   437     close(fd);
   438     if (haptic->hwdata != NULL) {
   439         SDL_free(haptic->hwdata);
   440         haptic->hwdata = NULL;
   441     }
   442     return -1;
   443 }
   444 
   445 
   446 /*
   447  * Opens a haptic device for usage.
   448  */
   449 int
   450 SDL_SYS_HapticOpen(SDL_Haptic * haptic)
   451 {
   452     int fd;
   453     int ret;
   454     SDL_hapticlist_item *item;
   455 
   456     item = HapticByDevIndex(haptic->index);
   457     /* Open the character device */
   458     fd = open(item->fname, O_RDWR, 0);
   459     if (fd < 0) {
   460         return SDL_SetError("Haptic: Unable to open %s: %s",
   461                             item->fname, strerror(errno));
   462     }
   463 
   464     /* Try to create the haptic. */
   465     ret = SDL_SYS_HapticOpenFromFD(haptic, fd); /* Already closes on error. */
   466     if (ret < 0) {
   467         return -1;
   468     }
   469 
   470     /* Set the fname. */
   471     haptic->hwdata->fname = SDL_strdup( item->fname );
   472     return 0;
   473 }
   474 
   475 
   476 /*
   477  * Opens a haptic device from first mouse it finds for usage.
   478  */
   479 int
   480 SDL_SYS_HapticMouse(void)
   481 {
   482     int fd;
   483     int device_index = 0;
   484     SDL_hapticlist_item *item;
   485 
   486     for (item = SDL_hapticlist; item; item = item->next) {
   487         /* Open the device. */
   488         fd = open(item->fname, O_RDWR, 0);
   489         if (fd < 0) {
   490             return SDL_SetError("Haptic: Unable to open %s: %s",
   491                                 item->fname, strerror(errno));
   492         }
   493 
   494         /* Is it a mouse? */
   495         if (EV_IsMouse(fd)) {
   496             close(fd);
   497             return device_index;
   498         }
   499 
   500         close(fd);
   501 
   502         ++device_index;
   503     }
   504 
   505     return -1;
   506 }
   507 
   508 
   509 /*
   510  * Checks to see if a joystick has haptic features.
   511  */
   512 int
   513 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
   514 {
   515     if (joystick->driver != &SDL_LINUX_JoystickDriver) {
   516         return 0;
   517     }
   518     return EV_IsHaptic(joystick->hwdata->fd);
   519 }
   520 
   521 
   522 /*
   523  * Checks to see if the haptic device and joystick are in reality the same.
   524  */
   525 int
   526 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
   527 {
   528     if (joystick->driver != &SDL_LINUX_JoystickDriver) {
   529         return 0;
   530     }
   531     /* We are assuming Linux is using evdev which should trump the old
   532      * joystick methods. */
   533     if (SDL_strcmp(joystick->hwdata->fname, haptic->hwdata->fname) == 0) {
   534         return 1;
   535     }
   536     return 0;
   537 }
   538 
   539 
   540 /*
   541  * Opens a SDL_Haptic from a SDL_Joystick.
   542  */
   543 int
   544 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
   545 {
   546     int device_index = 0;
   547     int fd;
   548     int ret;
   549     SDL_hapticlist_item *item;
   550     
   551     if (joystick->driver != &SDL_LINUX_JoystickDriver) {
   552         return -1;
   553     }
   554     /* Find the joystick in the haptic list. */
   555     for (item = SDL_hapticlist; item; item = item->next) {
   556         if (SDL_strcmp(item->fname, joystick->hwdata->fname) == 0) {
   557             break;
   558         }
   559         ++device_index;
   560     }
   561     haptic->index = device_index;
   562 
   563     if (device_index >= MAX_HAPTICS) {
   564         return SDL_SetError("Haptic: Joystick doesn't have Haptic capabilities");
   565     }
   566 
   567     fd = open(joystick->hwdata->fname, O_RDWR, 0);
   568     if (fd < 0) {
   569         return SDL_SetError("Haptic: Unable to open %s: %s",
   570                             joystick->hwdata->fname, strerror(errno));
   571     }
   572     ret = SDL_SYS_HapticOpenFromFD(haptic, fd); /* Already closes on error. */
   573     if (ret < 0) {
   574         return -1;
   575     }
   576 
   577     haptic->hwdata->fname = SDL_strdup( joystick->hwdata->fname );
   578 
   579     return 0;
   580 }
   581 
   582 
   583 /*
   584  * Closes the haptic device.
   585  */
   586 void
   587 SDL_SYS_HapticClose(SDL_Haptic * haptic)
   588 {
   589     if (haptic->hwdata) {
   590 
   591         /* Free effects. */
   592         SDL_free(haptic->effects);
   593         haptic->effects = NULL;
   594         haptic->neffects = 0;
   595 
   596         /* Clean up */
   597         close(haptic->hwdata->fd);
   598 
   599         /* Free */
   600         SDL_free(haptic->hwdata->fname);
   601         SDL_free(haptic->hwdata);
   602         haptic->hwdata = NULL;
   603     }
   604 
   605     /* Clear the rest. */
   606     SDL_memset(haptic, 0, sizeof(SDL_Haptic));
   607 }
   608 
   609 
   610 /*
   611  * Clean up after system specific haptic stuff
   612  */
   613 void
   614 SDL_SYS_HapticQuit(void)
   615 {
   616     SDL_hapticlist_item *item = NULL;
   617     SDL_hapticlist_item *next = NULL;
   618 
   619     for (item = SDL_hapticlist; item; item = next) {
   620         next = item->next;
   621         /* Opened and not closed haptics are leaked, this is on purpose.
   622          * Close your haptic devices after usage. */
   623         SDL_free(item->fname);
   624         SDL_free(item);
   625     }
   626 
   627 #if SDL_USE_LIBUDEV
   628     SDL_UDEV_DelCallback(haptic_udev_callback);
   629     SDL_UDEV_Quit();
   630 #endif /* SDL_USE_LIBUDEV */
   631 
   632     numhaptics = 0;
   633     SDL_hapticlist = NULL;
   634     SDL_hapticlist_tail = NULL;
   635 }
   636 
   637 
   638 /*
   639  * Converts an SDL button to a ff_trigger button.
   640  */
   641 static Uint16
   642 SDL_SYS_ToButton(Uint16 button)
   643 {
   644     Uint16 ff_button;
   645 
   646     ff_button = 0;
   647 
   648     /*
   649      * Not sure what the proper syntax is because this actually isn't implemented
   650      * in the current kernel from what I've seen (2.6.26).
   651      */
   652     if (button != 0) {
   653         ff_button = BTN_GAMEPAD + button - 1;
   654     }
   655 
   656     return ff_button;
   657 }
   658 
   659 
   660 /*
   661  * Initializes the ff_effect usable direction from a SDL_HapticDirection.
   662  */
   663 static int
   664 SDL_SYS_ToDirection(Uint16 *dest, SDL_HapticDirection * src)
   665 {
   666     Uint32 tmp;
   667 
   668     switch (src->type) {
   669     case SDL_HAPTIC_POLAR:
   670         /* Linux directions start from south.
   671                 (and range from 0 to 0xFFFF)
   672                    Quoting include/linux/input.h, line 926:
   673                    Direction of the effect is encoded as follows:
   674                         0 deg -> 0x0000 (down)
   675                         90 deg -> 0x4000 (left)
   676                         180 deg -> 0x8000 (up)
   677                         270 deg -> 0xC000 (right)
   678                    The force pulls into the direction specified by Linux directions,
   679                    i.e. the opposite convention of SDL directions.
   680                     */
   681         tmp = ((src->dir[0] % 36000) * 0x8000) / 18000; /* convert to range [0,0xFFFF] */
   682         *dest = (Uint16) tmp;
   683         break;
   684 
   685     case SDL_HAPTIC_SPHERICAL:
   686             /*
   687                 We convert to polar, because that's the only supported direction on Linux.
   688                 The first value of a spherical direction is practically the same as a
   689                 Polar direction, except that we have to add 90 degrees. It is the angle
   690                 from EAST {1,0} towards SOUTH {0,1}.
   691                 --> add 9000
   692                 --> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR.
   693             */
   694             tmp = ((src->dir[0]) + 9000) % 36000;    /* Convert to polars */
   695         tmp = (tmp * 0x8000) / 18000; /* convert to range [0,0xFFFF] */
   696         *dest = (Uint16) tmp;
   697         break;
   698 
   699     case SDL_HAPTIC_CARTESIAN:
   700         if (!src->dir[1])
   701             *dest = (src->dir[0] >= 0 ? 0x4000 : 0xC000);
   702         else if (!src->dir[0])
   703             *dest = (src->dir[1] >= 0 ? 0x8000 : 0);
   704         else {
   705             float f = SDL_atan2(src->dir[1], src->dir[0]);    /* Ideally we'd use fixed point math instead of floats... */
   706                     /*
   707                       atan2 takes the parameters: Y-axis-value and X-axis-value (in that order)
   708                        - Y-axis-value is the second coordinate (from center to SOUTH)
   709                        - X-axis-value is the first coordinate (from center to EAST)
   710                         We add 36000, because atan2 also returns negative values. Then we practically
   711                         have the first spherical value. Therefore we proceed as in case
   712                         SDL_HAPTIC_SPHERICAL and add another 9000 to get the polar value.
   713                       --> add 45000 in total
   714                       --> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR.
   715                     */
   716                 tmp = (((Sint32) (f * 18000. / M_PI)) + 45000) % 36000;
   717             tmp = (tmp * 0x8000) / 18000; /* convert to range [0,0xFFFF] */
   718             *dest = (Uint16) tmp;
   719         }
   720         break;
   721     case SDL_HAPTIC_STEERING_AXIS:
   722         *dest = 0x4000;
   723         break;
   724     default:
   725         return SDL_SetError("Haptic: Unsupported direction type.");
   726     }
   727 
   728     return 0;
   729 }
   730 
   731 
   732 #define  CLAMP(x)    (((x) > 32767) ? 32767 : x)
   733 /*
   734  * Initializes the Linux effect struct from a haptic_effect.
   735  * Values above 32767 (for unsigned) are unspecified so we must clamp.
   736  */
   737 static int
   738 SDL_SYS_ToFFEffect(struct ff_effect *dest, SDL_HapticEffect * src)
   739 {
   740     SDL_HapticConstant *constant;
   741     SDL_HapticPeriodic *periodic;
   742     SDL_HapticCondition *condition;
   743     SDL_HapticRamp *ramp;
   744     SDL_HapticLeftRight *leftright;
   745 
   746     /* Clear up */
   747     SDL_memset(dest, 0, sizeof(struct ff_effect));
   748 
   749     switch (src->type) {
   750     case SDL_HAPTIC_CONSTANT:
   751         constant = &src->constant;
   752 
   753         /* Header */
   754         dest->type = FF_CONSTANT;
   755         if (SDL_SYS_ToDirection(&dest->direction, &constant->direction) == -1)
   756             return -1;
   757 
   758         /* Replay */
   759         dest->replay.length = (constant->length == SDL_HAPTIC_INFINITY) ?
   760             0 : CLAMP(constant->length);
   761         dest->replay.delay = CLAMP(constant->delay);
   762 
   763         /* Trigger */
   764         dest->trigger.button = SDL_SYS_ToButton(constant->button);
   765         dest->trigger.interval = CLAMP(constant->interval);
   766 
   767         /* Constant */
   768         dest->u.constant.level = constant->level;
   769 
   770         /* Envelope */
   771         dest->u.constant.envelope.attack_length =
   772             CLAMP(constant->attack_length);
   773         dest->u.constant.envelope.attack_level =
   774             CLAMP(constant->attack_level);
   775         dest->u.constant.envelope.fade_length = CLAMP(constant->fade_length);
   776         dest->u.constant.envelope.fade_level = CLAMP(constant->fade_level);
   777 
   778         break;
   779 
   780     case SDL_HAPTIC_SINE:
   781     /* !!! FIXME: put this back when we have more bits in 2.1 */
   782     /* case SDL_HAPTIC_SQUARE: */
   783     case SDL_HAPTIC_TRIANGLE:
   784     case SDL_HAPTIC_SAWTOOTHUP:
   785     case SDL_HAPTIC_SAWTOOTHDOWN:
   786         periodic = &src->periodic;
   787 
   788         /* Header */
   789         dest->type = FF_PERIODIC;
   790         if (SDL_SYS_ToDirection(&dest->direction, &periodic->direction) == -1)
   791             return -1;
   792 
   793         /* Replay */
   794         dest->replay.length = (periodic->length == SDL_HAPTIC_INFINITY) ?
   795             0 : CLAMP(periodic->length);
   796         dest->replay.delay = CLAMP(periodic->delay);
   797 
   798         /* Trigger */
   799         dest->trigger.button = SDL_SYS_ToButton(periodic->button);
   800         dest->trigger.interval = CLAMP(periodic->interval);
   801 
   802         /* Periodic */
   803         if (periodic->type == SDL_HAPTIC_SINE)
   804             dest->u.periodic.waveform = FF_SINE;
   805         /* !!! FIXME: put this back when we have more bits in 2.1 */
   806         /* else if (periodic->type == SDL_HAPTIC_SQUARE)
   807             dest->u.periodic.waveform = FF_SQUARE; */
   808         else if (periodic->type == SDL_HAPTIC_TRIANGLE)
   809             dest->u.periodic.waveform = FF_TRIANGLE;
   810         else if (periodic->type == SDL_HAPTIC_SAWTOOTHUP)
   811             dest->u.periodic.waveform = FF_SAW_UP;
   812         else if (periodic->type == SDL_HAPTIC_SAWTOOTHDOWN)
   813             dest->u.periodic.waveform = FF_SAW_DOWN;
   814         dest->u.periodic.period = CLAMP(periodic->period);
   815         dest->u.periodic.magnitude = periodic->magnitude;
   816         dest->u.periodic.offset = periodic->offset;
   817         /* Linux phase is defined in interval "[0x0000, 0x10000[", corresponds with "[0deg, 360deg[" phase shift. */
   818         dest->u.periodic.phase = ((Uint32)periodic->phase * 0x10000U) / 36000;
   819 
   820         /* Envelope */
   821         dest->u.periodic.envelope.attack_length =
   822             CLAMP(periodic->attack_length);
   823         dest->u.periodic.envelope.attack_level =
   824             CLAMP(periodic->attack_level);
   825         dest->u.periodic.envelope.fade_length = CLAMP(periodic->fade_length);
   826         dest->u.periodic.envelope.fade_level = CLAMP(periodic->fade_level);
   827 
   828         break;
   829 
   830     case SDL_HAPTIC_SPRING:
   831     case SDL_HAPTIC_DAMPER:
   832     case SDL_HAPTIC_INERTIA:
   833     case SDL_HAPTIC_FRICTION:
   834         condition = &src->condition;
   835 
   836         /* Header */
   837         if (condition->type == SDL_HAPTIC_SPRING)
   838             dest->type = FF_SPRING;
   839         else if (condition->type == SDL_HAPTIC_DAMPER)
   840             dest->type = FF_DAMPER;
   841         else if (condition->type == SDL_HAPTIC_INERTIA)
   842             dest->type = FF_INERTIA;
   843         else if (condition->type == SDL_HAPTIC_FRICTION)
   844             dest->type = FF_FRICTION;
   845         dest->direction = 0;    /* Handled by the condition-specifics. */
   846 
   847         /* Replay */
   848         dest->replay.length = (condition->length == SDL_HAPTIC_INFINITY) ?
   849             0 : CLAMP(condition->length);
   850         dest->replay.delay = CLAMP(condition->delay);
   851 
   852         /* Trigger */
   853         dest->trigger.button = SDL_SYS_ToButton(condition->button);
   854         dest->trigger.interval = CLAMP(condition->interval);
   855 
   856         /* Condition */
   857         /* X axis */
   858         dest->u.condition[0].right_saturation = condition->right_sat[0];
   859         dest->u.condition[0].left_saturation = condition->left_sat[0];
   860         dest->u.condition[0].right_coeff = condition->right_coeff[0];
   861         dest->u.condition[0].left_coeff = condition->left_coeff[0];
   862         dest->u.condition[0].deadband = condition->deadband[0];
   863         dest->u.condition[0].center = condition->center[0];
   864         /* Y axis */
   865         dest->u.condition[1].right_saturation = condition->right_sat[1];
   866         dest->u.condition[1].left_saturation = condition->left_sat[1];
   867         dest->u.condition[1].right_coeff = condition->right_coeff[1];
   868         dest->u.condition[1].left_coeff = condition->left_coeff[1];
   869         dest->u.condition[1].deadband = condition->deadband[1];
   870         dest->u.condition[1].center = condition->center[1];
   871 
   872         /*
   873          * There is no envelope in the linux force feedback api for conditions.
   874          */
   875 
   876         break;
   877 
   878     case SDL_HAPTIC_RAMP:
   879         ramp = &src->ramp;
   880 
   881         /* Header */
   882         dest->type = FF_RAMP;
   883         if (SDL_SYS_ToDirection(&dest->direction, &ramp->direction) == -1)
   884             return -1;
   885 
   886         /* Replay */
   887         dest->replay.length = (ramp->length == SDL_HAPTIC_INFINITY) ?
   888             0 : CLAMP(ramp->length);
   889         dest->replay.delay = CLAMP(ramp->delay);
   890 
   891         /* Trigger */
   892         dest->trigger.button = SDL_SYS_ToButton(ramp->button);
   893         dest->trigger.interval = CLAMP(ramp->interval);
   894 
   895         /* Ramp */
   896         dest->u.ramp.start_level = ramp->start;
   897         dest->u.ramp.end_level = ramp->end;
   898 
   899         /* Envelope */
   900         dest->u.ramp.envelope.attack_length = CLAMP(ramp->attack_length);
   901         dest->u.ramp.envelope.attack_level = CLAMP(ramp->attack_level);
   902         dest->u.ramp.envelope.fade_length = CLAMP(ramp->fade_length);
   903         dest->u.ramp.envelope.fade_level = CLAMP(ramp->fade_level);
   904 
   905         break;
   906 
   907     case SDL_HAPTIC_LEFTRIGHT:
   908         leftright = &src->leftright;
   909 
   910         /* Header */
   911         dest->type = FF_RUMBLE;
   912         dest->direction = 0;
   913 
   914         /* Replay */
   915         dest->replay.length = (leftright->length == SDL_HAPTIC_INFINITY) ?
   916             0 : CLAMP(leftright->length);
   917 
   918         /* Trigger */
   919         dest->trigger.button = 0;
   920         dest->trigger.interval = 0;
   921 
   922         /* Rumble (Linux expects 0-65535, so multiply by 2) */
   923         dest->u.rumble.strong_magnitude = CLAMP(leftright->large_magnitude) * 2;
   924         dest->u.rumble.weak_magnitude = CLAMP(leftright->small_magnitude) * 2;
   925 
   926         break;
   927 
   928 
   929     default:
   930         return SDL_SetError("Haptic: Unknown effect type.");
   931     }
   932 
   933     return 0;
   934 }
   935 
   936 
   937 /*
   938  * Creates a new haptic effect.
   939  */
   940 int
   941 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
   942                         SDL_HapticEffect * base)
   943 {
   944     struct ff_effect *linux_effect;
   945 
   946     /* Allocate the hardware effect */
   947     effect->hweffect = (struct haptic_hweffect *)
   948         SDL_malloc(sizeof(struct haptic_hweffect));
   949     if (effect->hweffect == NULL) {
   950         return SDL_OutOfMemory();
   951     }
   952 
   953     /* Prepare the ff_effect */
   954     linux_effect = &effect->hweffect->effect;
   955     if (SDL_SYS_ToFFEffect(linux_effect, base) != 0) {
   956         goto new_effect_err;
   957     }
   958     linux_effect->id = -1;      /* Have the kernel give it an id */
   959 
   960     /* Upload the effect */
   961     if (ioctl(haptic->hwdata->fd, EVIOCSFF, linux_effect) < 0) {
   962         SDL_SetError("Haptic: Error uploading effect to the device: %s",
   963                      strerror(errno));
   964         goto new_effect_err;
   965     }
   966 
   967     return 0;
   968 
   969   new_effect_err:
   970     SDL_free(effect->hweffect);
   971     effect->hweffect = NULL;
   972     return -1;
   973 }
   974 
   975 
   976 /*
   977  * Updates an effect.
   978  *
   979  * Note: Dynamically updating the direction can in some cases force
   980  * the effect to restart and run once.
   981  */
   982 int
   983 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
   984                            struct haptic_effect *effect,
   985                            SDL_HapticEffect * data)
   986 {
   987     struct ff_effect linux_effect;
   988 
   989     /* Create the new effect */
   990     if (SDL_SYS_ToFFEffect(&linux_effect, data) != 0) {
   991         return -1;
   992     }
   993     linux_effect.id = effect->hweffect->effect.id;
   994 
   995     /* See if it can be uploaded. */
   996     if (ioctl(haptic->hwdata->fd, EVIOCSFF, &linux_effect) < 0) {
   997         return SDL_SetError("Haptic: Error updating the effect: %s",
   998                             strerror(errno));
   999     }
  1000 
  1001     /* Copy the new effect into memory. */
  1002     SDL_memcpy(&effect->hweffect->effect, &linux_effect,
  1003                sizeof(struct ff_effect));
  1004 
  1005     return effect->hweffect->effect.id;
  1006 }
  1007 
  1008 
  1009 /*
  1010  * Runs an effect.
  1011  */
  1012 int
  1013 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
  1014                         Uint32 iterations)
  1015 {
  1016     struct input_event run;
  1017 
  1018     /* Prepare to run the effect */
  1019     run.type = EV_FF;
  1020     run.code = effect->hweffect->effect.id;
  1021     /* We don't actually have infinity here, so we just do INT_MAX which is pretty damn close. */
  1022     run.value = (iterations > INT_MAX) ? INT_MAX : iterations;
  1023 
  1024     if (write(haptic->hwdata->fd, (const void *) &run, sizeof(run)) < 0) {
  1025         return SDL_SetError("Haptic: Unable to run the effect: %s", strerror(errno));
  1026     }
  1027 
  1028     return 0;
  1029 }
  1030 
  1031 
  1032 /*
  1033  * Stops an effect.
  1034  */
  1035 int
  1036 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1037 {
  1038     struct input_event stop;
  1039 
  1040     stop.type = EV_FF;
  1041     stop.code = effect->hweffect->effect.id;
  1042     stop.value = 0;
  1043 
  1044     if (write(haptic->hwdata->fd, (const void *) &stop, sizeof(stop)) < 0) {
  1045         return SDL_SetError("Haptic: Unable to stop the effect: %s",
  1046                             strerror(errno));
  1047     }
  1048 
  1049     return 0;
  1050 }
  1051 
  1052 
  1053 /*
  1054  * Frees the effect.
  1055  */
  1056 void
  1057 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
  1058 {
  1059     if (ioctl(haptic->hwdata->fd, EVIOCRMFF, effect->hweffect->effect.id) < 0) {
  1060         SDL_SetError("Haptic: Error removing the effect from the device: %s",
  1061                      strerror(errno));
  1062     }
  1063     SDL_free(effect->hweffect);
  1064     effect->hweffect = NULL;
  1065 }
  1066 
  1067 
  1068 /*
  1069  * Gets the status of a haptic effect.
  1070  */
  1071 int
  1072 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
  1073                               struct haptic_effect *effect)
  1074 {
  1075 #if 0                           /* Not supported atm. */
  1076     struct input_event ie;
  1077 
  1078     ie.type = EV_FF;
  1079     ie.type = EV_FF_STATUS;
  1080     ie.code = effect->hweffect->effect.id;
  1081 
  1082     if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
  1083         return SDL_SetError("Haptic: Error getting device status.");
  1084     }
  1085 
  1086     return 0;
  1087 #endif
  1088 
  1089     return -1;
  1090 }
  1091 
  1092 
  1093 /*
  1094  * Sets the gain.
  1095  */
  1096 int
  1097 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
  1098 {
  1099     struct input_event ie;
  1100 
  1101     ie.type = EV_FF;
  1102     ie.code = FF_GAIN;
  1103     ie.value = (0xFFFFUL * gain) / 100;
  1104 
  1105     if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
  1106         return SDL_SetError("Haptic: Error setting gain: %s", strerror(errno));
  1107     }
  1108 
  1109     return 0;
  1110 }
  1111 
  1112 
  1113 /*
  1114  * Sets the autocentering.
  1115  */
  1116 int
  1117 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
  1118 {
  1119     struct input_event ie;
  1120 
  1121     ie.type = EV_FF;
  1122     ie.code = FF_AUTOCENTER;
  1123     ie.value = (0xFFFFUL * autocenter) / 100;
  1124 
  1125     if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
  1126         return SDL_SetError("Haptic: Error setting autocenter: %s", strerror(errno));
  1127     }
  1128 
  1129     return 0;
  1130 }
  1131 
  1132 
  1133 /*
  1134  * Pausing is not supported atm by linux.
  1135  */
  1136 int
  1137 SDL_SYS_HapticPause(SDL_Haptic * haptic)
  1138 {
  1139     return -1;
  1140 }
  1141 
  1142 
  1143 /*
  1144  * Unpausing is not supported atm by linux.
  1145  */
  1146 int
  1147 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
  1148 {
  1149     return -1;
  1150 }
  1151 
  1152 
  1153 /*
  1154  * Stops all the currently playing effects.
  1155  */
  1156 int
  1157 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
  1158 {
  1159     int i, ret;
  1160 
  1161     /* Linux does not support this natively so we have to loop. */
  1162     for (i = 0; i < haptic->neffects; i++) {
  1163         if (haptic->effects[i].hweffect != NULL) {
  1164             ret = SDL_SYS_HapticStopEffect(haptic, &haptic->effects[i]);
  1165             if (ret < 0) {
  1166                 return SDL_SetError
  1167                     ("Haptic: Error while trying to stop all playing effects.");
  1168             }
  1169         }
  1170     }
  1171     return 0;
  1172 }
  1173 
  1174 #endif /* SDL_HAPTIC_LINUX */
  1175 
  1176 /* vi: set ts=4 sw=4 expandtab: */