Re: [AD] Linux joysytick and force feedback

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


OK, thanks for the dump of evtest! I could see the device has absolute
axes (ABS_VOLUME) and buttons, so it would be recognised as a
joystick. I tried several other devices, such as my own wireless
keyboard and the mouse pad on my laptop, and it turns out that they
were being detected as joysticks as well.

I looked at SDL, but their joystick checking might be defeated by say,
a mouse pad that has ABS_X and ABS_Y axes. So, I implemented a more
severe algorithm. I only accept as a joystick any device that has BOTH
joystick-like axes AND  joystick-like buttons. I detect this by
limiting the acceptable axes and buttons to ranges of known values
from the Linux input.h header file. I hope this will be enough but if
there are more false positives, then I can make it even more severe by
rejecting even more button ranges or requiring at least 2 axes.

I also enhanced ex_joystick_events a bit to show some textual
information about the joystick.
This will make it easier to see if we have a false positive in the
future as the joystick name will tell us.

In attachment two patches formatted though git format-patch --stdout.
I hope they can be applied correctly this time.

Kind Regards,

B.
From 253efaf719f70c5eac68b33bb7efbc5e441e31d7 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Fri, 21 Jun 2013 15:02:42 +0200
Subject: [PATCH 1/6] Changed linux josytick driver to use the
 /dev/input/eventX API in stead of the old
 /dev/input/jsX API.

---
 include/allegro5/internal/aintern_ljoynu.h |   60 +++++
 src/linux/ljoynu.c                         |  328 +++++++++++++++-------------
 2 files changed, 234 insertions(+), 154 deletions(-)
 create mode 100644 include/allegro5/internal/aintern_ljoynu.h

diff --git a/include/allegro5/internal/aintern_ljoynu.h b/include/allegro5/internal/aintern_ljoynu.h
new file mode 100644
index 0000000..c16c372
--- /dev/null
+++ b/include/allegro5/internal/aintern_ljoynu.h
@@ -0,0 +1,60 @@
+#ifndef __al_included_allegro_aintern_ljoynu_h
+#define __al_included_allegro_aintern_ljoynu_h
+
+
+#include "allegro5/joystick.h"
+#include "allegro5/internal/aintern_joystick.h"
+
+
+
+
+/* State transitions:
+ *    unused -> born
+ *    born -> alive
+ *    born -> dying
+ *    active -> dying
+ *    dying -> unused
+ */
+typedef enum {
+   LJOY_STATE_UNUSED,
+   LJOY_STATE_BORN,
+   LJOY_STATE_ALIVE,
+   LJOY_STATE_DYING
+} CONFIG_STATE;
+
+#define ACTIVE_STATE(st) \
+   ((st) == LJOY_STATE_ALIVE || (st) == LJOY_STATE_DYING)
+
+
+/* Map a Linux joystick axis number to an Allegro (stick,axis) pair 
+ * Uses the input event interface's numbering. ABS_MISC = 0x28,
+ * So that is the maximum of allowed axes on Linux.
+ */
+#define TOTAL_JOYSTICK_AXES  0x28
+
+typedef struct {
+   int stick;
+   int axis;
+   int value;
+   int min;
+   int max;    
+   int fuzz;
+   int flat;
+} AXIS_MAPPING;
+
+
+typedef struct ALLEGRO_JOYSTICK_LINUX
+{
+   ALLEGRO_JOYSTICK parent;
+   int config_state;
+   bool marked;
+   int fd;
+   ALLEGRO_USTR *device_name;
+
+   AXIS_MAPPING axis_mapping[TOTAL_JOYSTICK_AXES];
+   ALLEGRO_JOYSTICK_STATE joystate;
+   char name[100];
+} ALLEGRO_JOYSTICK_LINUX;
+
+
+#endif
\ No newline at end of file
diff --git a/src/linux/ljoynu.c b/src/linux/ljoynu.c
index f291f81..8cd6fd0 100644
--- a/src/linux/ljoynu.c
+++ b/src/linux/ljoynu.c
@@ -41,6 +41,7 @@
  */
 #include <sys/types.h>
 #include <linux/joystick.h>
+#include <linux/input.h>
 
 #if defined(ALLEGRO_HAVE_SYS_INOTIFY_H) && defined(ALLEGRO_HAVE_SYS_TIMERFD_H)
    #define SUPPORT_HOTPLUG
@@ -50,47 +51,7 @@
 
 ALLEGRO_DEBUG_CHANNEL("ljoy");
 
-#define TOTAL_JOYSTICK_AXES  (_AL_MAX_JOYSTICK_STICKS * _AL_MAX_JOYSTICK_AXES)
-
-
-/* State transitions:
- *    unused -> born
- *    born -> alive
- *    born -> dying
- *    active -> dying
- *    dying -> unused
- */
-typedef enum {
-   LJOY_STATE_UNUSED,
-   LJOY_STATE_BORN,
-   LJOY_STATE_ALIVE,
-   LJOY_STATE_DYING
-} CONFIG_STATE;
-
-#define ACTIVE_STATE(st) \
-   ((st) == LJOY_STATE_ALIVE || (st) == LJOY_STATE_DYING)
-
-
-/* map a Linux joystick axis number to an Allegro (stick,axis) pair */
-typedef struct {
-   int stick;
-   int axis;
-} AXIS_MAPPING;
-
-
-typedef struct ALLEGRO_JOYSTICK_LINUX
-{
-   ALLEGRO_JOYSTICK parent;
-   int config_state;
-   bool marked;
-   int fd;
-   ALLEGRO_USTR *device_name;
-
-   AXIS_MAPPING axis_mapping[TOTAL_JOYSTICK_AXES];
-   ALLEGRO_JOYSTICK_STATE joystate;
-   char name[100];
-} ALLEGRO_JOYSTICK_LINUX;
-
+#include "allegro5/internal/aintern_ljoynu.h"
 
 
 /* forward declarations */
@@ -139,34 +100,43 @@ static int timer_fd = -1;
 #endif
 
 
-/* check_js_api_version: [primary thread]
+
+#define LONG_BITS (sizeof(long) * 8)
+#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
+/* Tests if a bit in an array of longs is set. */
+#define TEST_BIT(nr, addr) \
+    (((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0)
+
+/* check_is_event_joystick: 
  *
- *  Return true if the joystick API used by the device is supported by
- *  this driver.
+ *  Return true if this fd supports the /dev/inputt/eventxx API and 
+ * is a joystick indeed.
  */
-static bool check_js_api_version(int fd)
-{
-   unsigned int raw_version;
-
-   if (ioctl(fd, JSIOCGVERSION, &raw_version) < 0) {
-      /* NOTE: IOCTL fails if the joystick API is version 0.x */
-      ALLEGRO_WARN("Your Linux joystick API is version 0.x which is unsupported.\n");
-      return false;
+static bool check_is_event_joystick(int fd) {
+
+  
+  // unsigned long device_bits[(EV_MAX + 8) / sizeof(unsigned long)];  
+  unsigned long bitmask[NLONGS(EV_CNT)]   = {0};
+  
+  if (ioctl (fd, EVIOCGBIT(0, sizeof(bitmask)), bitmask) < 0) {
+    perror ("EVIOCGBIT ioctl failed");
+    fprintf(stderr, "For fd %d\n", fd);
+    return false;
+  }
+  /* If we see buttons and axes, it's most likely a joystick */
+   if (TEST_BIT(EV_ABS, bitmask) && TEST_BIT(EV_KEY, bitmask)) {   
+     return true;
    }
-
-   /*
-   struct { unsigned char build, minor, major; } version;
-
-   version.major = (raw_version & 0xFF0000) >> 16;
-   version.minor = (raw_version & 0xFF00) >> 8;
-   version.build = (raw_version & 0xFF);
-   */
-
-   return true;
+   
+   /* Force feedback device is always a joystick. */
+   if (TEST_BIT(EV_FF, bitmask)) {
+     return true;
+   }
+   
+   return false;
 }
 
 
-
 static bool ljoy_detect_device_name(int num, ALLEGRO_USTR *device_name)
 {
    ALLEGRO_CONFIG *cfg;
@@ -185,7 +155,7 @@ static bool ljoy_detect_device_name(int num, ALLEGRO_USTR *device_name)
    }
 
    if (al_ustr_size(device_name) == 0)
-      al_ustr_appendf(device_name, "/dev/input/js%d", num);
+      al_ustr_appendf(device_name, "/dev/input/event%d", num);
 
    return (stat(al_cstr(device_name), &stbuf) == 0);
 }
@@ -267,6 +237,26 @@ static void inactivate_joy(ALLEGRO_JOYSTICK_LINUX *joy)
 }
 
 
+static bool get_num_buttons(int fd, int * num_buttons)
+{
+    unsigned long key_bits[NLONGS(KEY_CNT)]  = {0};
+    int nbut = 0;
+    
+    int res, i;
+ 
+    res = ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), key_bits);
+    if (res < 0) return false;
+    
+    for (i = BTN_MISC ; i <= KEY_MAX; i++) {
+      if(TEST_BIT(i, key_bits)) {
+          nbut++;
+      }
+    }
+    (*num_buttons) = nbut;
+    return true;
+}
+
+
 
 static void ljoy_scan(bool configure)
 {
@@ -285,8 +275,8 @@ static void ljoy_scan(bool configure)
 
    device_name = al_ustr_new("");
 
-   /* This is a big number, but there can be gaps. */
-   for (num = 0; num < 16; num++) {
+   /* This is a big number, but there can be gaps, and other unrelated event queues. */
+   for (num = 0; num < 32; num++) {
       if (!ljoy_detect_device_name(num, device_name))
          continue;
 
@@ -297,16 +287,20 @@ static void ljoy_scan(bool configure)
          continue;
       }
 
-      /* Try to open the device. */
-      fd = open(al_cstr(device_name), O_RDONLY|O_NONBLOCK);
+      /* Try to open the device. The device must be pened in O_RDWR mode to allow 
+       * writing of haptic effects! The haptic driver for linux 
+       * reuses the joystick driver's FD. */
+      fd = open(al_cstr(device_name), O_RDWR|O_NONBLOCK);
       if (fd == -1) {
          ALLEGRO_WARN("Failed to open device %s\n", al_cstr(device_name));
          continue;
       }
-      if (!check_js_api_version(fd)) {
+ 
+      if (!check_is_event_joystick(fd)) {
          close(fd);
          continue;
       }
+ 
       ALLEGRO_DEBUG("Device %s is new\n", al_cstr(device_name));
 
       joy = ljoy_allocate_structure();
@@ -316,76 +310,94 @@ static void ljoy_scan(bool configure)
       joy->marked = true;
       config_needs_merging = true;
 
-      if (ioctl(fd, JSIOCGNAME(sizeof(joy->name)), joy->name) < 0)
+      if (ioctl(fd, EVIOCGNAME(sizeof(joy->name)), joy->name) < 0)
          strcpy(joy->name, "Unknown");
 
       /* Fill in the joystick information fields. */
       {
-         char num_axes;
-         char num_buttons;
-         int throttle;
-         int s, a, b;
-   
-         ioctl(fd, JSIOCGAXES, &num_axes);
-         ioctl(fd, JSIOCGBUTTONS, &num_buttons);
+         int num_axes = 0;
+         int num_buttons;
+         int b;
+         unsigned long abs_bits[NLONGS(ABS_CNT)]  = {0};
+         int res, i;  
+         int stick = 0;
+         int axis  = 0;
+         
    
-         if (num_axes > TOTAL_JOYSTICK_AXES)
-            num_axes = TOTAL_JOYSTICK_AXES;
+         // get_num_axes(fd, &num_axes);
+         get_num_buttons(fd, &num_buttons);
    
          if (num_buttons > _AL_MAX_JOYSTICK_BUTTONS)
             num_buttons = _AL_MAX_JOYSTICK_BUTTONS;
-   
-         /* XXX use configuration system when we get one */
-         throttle = -1;
-   #if 0
-         /* User is allowed to override our simple assumption of which
-          * axis number (kernel) the throttle is located at. */
-         snprintf(tmp, sizeof(tmp), "throttle_axis_%d", num);
-         throttle = get_config_int("joystick", tmp, -1);
-         if (throttle == -1) {
-            throttle = get_config_int("joystick", 
-                                      "throttle_axis", -1);
-         }
-   #endif
-   
-         /* Each pair of axes is assumed to make up a stick unless it 
-          * is the sole remaining axis, or has been user specified, in 
-          * which case it is a throttle. */
-   
-         for (s = 0, a = 0;
-              s < _AL_MAX_JOYSTICK_STICKS && a < num_axes;
-              s++)
-         {
-            if ((a == throttle) || (a == num_axes-1)) {
-               /* One axis throttle. */
-               joy->parent.info.stick[s].flags = ALLEGRO_JOYFLAG_ANALOGUE;
-               joy->parent.info.stick[s].num_axes = 1;
-               joy->parent.info.stick[s].axis[0].name = "Throttle";
-               char *name = joy->parent.info.stick[s].axis[0].name;
-               joy->parent.info.stick[s].name = al_malloc(strlen(name) + 1);
-               strcpy(joy->parent.info.stick[s].name, name);
-               joy->axis_mapping[a].stick = s;
-               joy->axis_mapping[a].axis = 0;
-               a++;
-            }
-            else {
-               /* Two axis stick. */
-               joy->parent.info.stick[s].flags = ALLEGRO_JOYFLAG_ANALOGUE;
-               joy->parent.info.stick[s].num_axes = 2;
-               joy->parent.info.stick[s].axis[0].name = "X";
-               joy->parent.info.stick[s].axis[1].name = "Y";
-               joy->parent.info.stick[s].name = al_malloc (32);
-               snprintf((char *)joy->parent.info.stick[s].name, 32, "Stick %d", s+1);
-               joy->axis_mapping[a].stick = s;
-               joy->axis_mapping[a].axis = 0;
-               a++;
-               joy->axis_mapping[a].stick = s;
-               joy->axis_mapping[a].axis = 1;
-               a++;
+         
+         /* Scan the axes to get their properties. */
+        res = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits);    
+        if (res < 0) continue;
+        for (i = 0; i <= ABS_MISC; i++) {
+          if(TEST_BIT(i, abs_bits)) {
+          struct input_absinfo absinfo;
+          if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0)
+                    continue;
+            if (
+                 (i == ABS_THROTTLE) || (i == ABS_RUDDER) || (i == ABS_WHEEL) 
+              || (i == ABS_GAS)      || (i == ABS_BRAKE) || (i== ABS_BRAKE) 
+              || (i == ABS_PRESSURE) || (i == ABS_DISTANCE) || (i== ABS_TOOL_WIDTH) 
+            ) { 
+              /* One axis throttle. */
+               joy->parent.info.stick[stick].flags = ALLEGRO_JOYFLAG_ANALOGUE;
+               joy->parent.info.stick[stick].num_axes = 1;
+               joy->parent.info.stick[stick].axis[0].name = "Throttle";
+               char *name = joy->parent.info.stick[stick].axis[0].name;
+               joy->parent.info.stick[stick].name = al_malloc(strlen(name) + 1);
+               strcpy(joy->parent.info.stick[stick].name, name);
+               joy->axis_mapping[i].stick = stick;
+               joy->axis_mapping[i].axis  = 0;
+               joy->axis_mapping[i].min   = absinfo.minimum;
+               joy->axis_mapping[i].max   = absinfo.maximum;
+               joy->axis_mapping[i].value = absinfo.value;
+               joy->axis_mapping[i].fuzz  = absinfo.fuzz;
+               joy->axis_mapping[i].flat  = absinfo.flat;
+              /* Consider all these types of axis as throttle axis. */
+               stick++;
+            } else { /* regular axis, two axis stick. */
+              int digital = ((i >= ABS_HAT0X) && (i <=  ABS_HAT3Y));
+               if (axis == 0) { /* first axis of new joystick */
+                if (digital) {
+                  joy->parent.info.stick[stick].flags = ALLEGRO_JOYFLAG_DIGITAL;
+                } else { 
+                  joy->parent.info.stick[stick].flags = ALLEGRO_JOYFLAG_ANALOGUE;
+                }
+                joy->parent.info.stick[stick].num_axes = 2;
+                joy->parent.info.stick[stick].axis[0].name = "X";
+                joy->parent.info.stick[stick].axis[1].name = "Y";
+                joy->parent.info.stick[stick].name = al_malloc (32);
+                snprintf((char *)joy->parent.info.stick[stick].name, 32, "Stick %d", stick+1);
+                joy->axis_mapping[i].stick = stick;
+                joy->axis_mapping[i].axis  = axis;
+                joy->axis_mapping[i].min   = absinfo.minimum;
+                joy->axis_mapping[i].max   = absinfo.maximum;
+                joy->axis_mapping[i].value = absinfo.value;
+                joy->axis_mapping[i].fuzz  = absinfo.fuzz;
+                joy->axis_mapping[i].flat  = absinfo.flat;                
+                axis++;
+               } else { /* Second axis. */
+                joy->axis_mapping[i].stick = stick;
+                joy->axis_mapping[i].axis  = axis;
+                joy->axis_mapping[i].min   = absinfo.minimum;
+                joy->axis_mapping[i].max   = absinfo.maximum;
+                joy->axis_mapping[i].value = absinfo.value;
+                joy->axis_mapping[i].fuzz  = absinfo.fuzz;
+                joy->axis_mapping[i].flat  = absinfo.flat;
+                stick++;
+                axis = 0;
+               }
             }
-         }
+            num_axes++;
+          }
+        }
+    
    
-         joy->parent.info.num_sticks = s;
+        joy->parent.info.num_sticks = stick;
    
          /* Do the buttons. */
    
@@ -710,44 +722,52 @@ static void ljoy_process_new_data(void *data)
    
    _al_event_source_lock(es);
    {
-      struct js_event js_events[32];
+      struct input_event input_events[32];
       int bytes, nr, i;
 
-      while ((bytes = read(joy->fd, &js_events, sizeof js_events)) > 0) {
+      while ((bytes = read(joy->fd, &input_events, sizeof input_events)) > 0) {
 
-         nr = bytes / sizeof(struct js_event);
+         nr = bytes / sizeof(struct input_event);
 
          for (i = 0; i < nr; i++) {
-
-            int type   = js_events[i].type;
-            int number = js_events[i].number;
-            int value  = js_events[i].value;
-
-            if (type & JS_EVENT_BUTTON) {
-               if (number < _AL_MAX_JOYSTICK_BUTTONS) {
-                  if (value)
+            int type   = input_events[i].type;
+            int code   = input_events[i].code;
+            int value  = input_events[i].value;
+            if (type == EV_KEY) {
+              int number = code - BTN_JOYSTICK;
+              if (number < _AL_MAX_JOYSTICK_BUTTONS) {              
+              if (value)
                      joy->joystate.button[number] = 32767;
                   else
                      joy->joystate.button[number] = 0;
 
-                  ljoy_generate_button_event(joy, number,
+              ljoy_generate_button_event(joy, number,
                                              (value
                                               ? ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN
                                               : ALLEGRO_EVENT_JOYSTICK_BUTTON_UP));
-               }
+              } 
+            } else if ((type == EV_ABS ) && (code < ABS_MISC)) {
+                int       stick = -1;
+                int       axis  = -1;
+                float     range, pos;
+                
+              
+                AXIS_MAPPING * map = joy->axis_mapping +code;
+                axis  = map->axis;
+                stick = map->stick;                
+                range = (float) map->max - (float) map->min;
+                /* Normalize around 0. */
+                pos   = (float) value    - (float) map->min;
+                /* Divide by range, to get value between 0.0 and 1.0  */
+                pos   = pos              / range;
+                /* Now multiply by 2.0 and substract 1.0 to get a value between 
+                * -1.0 and 1.0
+                */
+                pos   = pos * 2.0f - 1.0f;
+                joy->joystate.stick[stick].axis[axis] = pos;
+                ljoy_generate_axis_event(joy, stick, axis, pos);           
             }
-            else if (type & JS_EVENT_AXIS) {
-               if (number < TOTAL_JOYSTICK_AXES) {
-                  int stick = joy->axis_mapping[number].stick;
-                  int axis  = joy->axis_mapping[number].axis;
-                  float pos = value / 32767.0;
-
-                  joy->joystate.stick[stick].axis[axis] = pos;
-
-                  ljoy_generate_axis_event(joy, stick, axis, pos);
-               }
-            }
-         }
+         }   
       }
    }
    _al_event_source_unlock(es);
-- 
1.7.10.4


From da8fc808e70079b6309bf467480d8873f83b874d Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Fri, 21 Jun 2013 15:05:38 +0200
Subject: [PATCH 2/6] Changed linux joystick driver to use the /dev/eventX API
 in stead of the old /dev/jsX API.

---
 src/linux/ljoynu.c |    5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/linux/ljoynu.c b/src/linux/ljoynu.c
index 8cd6fd0..6d62b7c 100644
--- a/src/linux/ljoynu.c
+++ b/src/linux/ljoynu.c
@@ -275,7 +275,10 @@ static void ljoy_scan(bool configure)
 
    device_name = al_ustr_new("");
 
-   /* This is a big number, but there can be gaps, and other unrelated event queues. */
+   /* This is a big number, but there can be gaps, and other unrelated event queues. 
+    * Perhaps it would be better to use glob() here in stead o gessing the numbers 
+    * like this?
+    */
    for (num = 0; num < 32; num++) {
       if (!ljoy_detect_device_name(num, device_name))
          continue;
-- 
1.7.10.4


From 4d33fc460e9a6db8e0b69373ab598a82d2f2e9ed Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Wed, 26 Jun 2013 08:31:23 +0200
Subject: [PATCH 3/6] Improved joystick detection by ignoring devices that
 don't have joystick related buttons and axes.

---
 src/linux/ljoynu.c |  128 ++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 94 insertions(+), 34 deletions(-)

diff --git a/src/linux/ljoynu.c b/src/linux/ljoynu.c
index 6d62b7c..85adf5b 100644
--- a/src/linux/ljoynu.c
+++ b/src/linux/ljoynu.c
@@ -107,32 +107,113 @@ static int timer_fd = -1;
 #define TEST_BIT(nr, addr) \
     (((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0)
 
+    
+    
+/* Gets the amount of joystick related keys/buttons. Only accept joystick-related 
+ * buttons, from BTN_MISC to BTN_9 for miscellaneous input, BTN_JOYSTICK to 
+ * BTN_DEAD, for joysticks, from BTN_GAMEPAD to BTN_THUMBR for game pads, 
+ * BTN_WHEEL, BTN_GEAR_DOWN, and BTN_GEAR_UP for steering wheels, and 
+ * BTN_TRIGGER_HAPPY_XXX buttons just in case some joysticks use these as well. 
+ */
+static bool get_num_buttons(int fd, int * num_buttons)
+{
+    unsigned long key_bits[NLONGS(KEY_CNT)]  = {0};
+    int nbut = 0;
+    
+    int res, i;
+ 
+    res = ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), key_bits);
+    if (res < 0) return false;
+        
+    for (i = BTN_MISC; i <= BTN_GEAR_UP; i++) {
+      bool is_wheel, is_joystick, is_gamepad, is_misc, is_triggerhappy;
+      
+      if(TEST_BIT(i, key_bits)) {
+        /*The device has this button. Determine kind of button by checking the range. */
+        is_misc           = ((i >= BTN_MISC)     && (i <=  BTN_9));
+        is_joystick       = ((i >= BTN_JOYSTICK) && (i <= BTN_DEAD));
+        is_gamepad        = ((i >= BTN_GAMEPAD)  && (i <= BTN_THUMBR));
+        is_wheel          = ((i >= BTN_WHEEL)    && (i <= BTN_GEAR_UP));
+        is_triggerhappy   = ((i >= BTN_TRIGGER_HAPPY)  && (i <= BTN_TRIGGER_HAPPY40));
+        if (!(is_misc || is_joystick || is_gamepad || is_wheel || is_triggerhappy) ) {
+          /* Ignore any buttons that don't seem joystick related. */
+          continue;
+        }        
+        nbut++;
+      }
+    }
+    (*num_buttons) = nbut;
+    return true;
+}
+
+/* Check the amount of joystick-related absolute axes. 
+ * Note that some devices, like (wireless) keyboards, may actually have absolute
+ * axes, such as a volume axis for the volume up / volume down buttons. 
+ * Also, some devices like a PS3 controller report many unusual axis, probably 
+ * used in nonstandard ways by the PS3 console. All these type of axes are  
+ * ignored by this function and by this driver. 
+ */
+static bool get_num_axes(int fd, int * num_axes) 
+{
+    int res, i;
+    unsigned long abs_bits[NLONGS(ABS_CNT)]  = {0};
+    int axes = 0;
+  
+    /* Only accept the axes from ABS_X up unto ABS_HAT3Y as real joystick
+    * axes. The following axes, ABS_PRESSURE, ABS_DISTANCE, ABS_TILT_X, 
+    * ABS_TILT_Y, ABS_TOOL_WIDTH seem to be for use by a drawing tablet like a 
+    * Wacom. ABS_VOLUME is for volume sliders or key pairs on some keyboards. 
+    * Other axes up to ABS_MISC may exist, such as on the PS3, 
+    * but they are most likely not useful.
+    */ 
+    
+    /* Scan the axes to get their properties. */
+    res = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits);    
+    if (res < 0) return false;
+    for (i = ABS_X; i <= ABS_HAT3Y; i++) 
+    {
+        if(TEST_BIT(i, abs_bits)) 
+        {
+           axes++;
+        }
+    }
+  /* Finally store the aount of axes. */
+  (*num_axes) = axes;
+  return true;
+}
+
+
+
+    
 /* check_is_event_joystick: 
  *
  *  Return true if this fd supports the /dev/inputt/eventxx API and 
  * is a joystick indeed.
  */
 static bool check_is_event_joystick(int fd) {
-
-  
-  // unsigned long device_bits[(EV_MAX + 8) / sizeof(unsigned long)];  
   unsigned long bitmask[NLONGS(EV_CNT)]   = {0};
   
   if (ioctl (fd, EVIOCGBIT(0, sizeof(bitmask)), bitmask) < 0) {
-    perror ("EVIOCGBIT ioctl failed");
-    fprintf(stderr, "For fd %d\n", fd);
     return false;
   }
-  /* If we see buttons and axes, it's most likely a joystick */
+  
+  /* If there are buttons and axes, it may be a joystick. Some of 
+   these axes may be not joystick-related though, so investigate further. */
    if (TEST_BIT(EV_ABS, bitmask) && TEST_BIT(EV_KEY, bitmask)) {   
-     return true;
-   }
-   
-   /* Force feedback device is always a joystick. */
-   if (TEST_BIT(EV_FF, bitmask)) {
-     return true;
+     int axes = 0, buttons = 0; 
+     /* Check the axes and buttons to see it it really is a joystick. */
+     if(!get_num_buttons(fd, &buttons)) return false;
+     if(!get_num_axes(fd, &axes)) return false;
+     /* The device must have at least 1 joystick related axis, and 1 joystick 
+      * related button. This is needed because some devices, such as mouse pads, 
+      * have ABS_X and ABS_Y axes just like a joystick would, but they don't have 
+      * joystick related buttons. By checking if the device has both joystick
+      *related axes and butons, such mouse pads can be excluded. 
+      */
+     return ((axes > 0) && (buttons > 0));
    }
    
+   /* No axes, no buttons, no joystick, sorry. */
    return false;
 }
 
@@ -237,27 +318,6 @@ static void inactivate_joy(ALLEGRO_JOYSTICK_LINUX *joy)
 }
 
 
-static bool get_num_buttons(int fd, int * num_buttons)
-{
-    unsigned long key_bits[NLONGS(KEY_CNT)]  = {0};
-    int nbut = 0;
-    
-    int res, i;
- 
-    res = ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), key_bits);
-    if (res < 0) return false;
-    
-    for (i = BTN_MISC ; i <= KEY_MAX; i++) {
-      if(TEST_BIT(i, key_bits)) {
-          nbut++;
-      }
-    }
-    (*num_buttons) = nbut;
-    return true;
-}
-
-
-
 static void ljoy_scan(bool configure)
 {
    int fd;
@@ -315,7 +375,7 @@ static void ljoy_scan(bool configure)
 
       if (ioctl(fd, EVIOCGNAME(sizeof(joy->name)), joy->name) < 0)
          strcpy(joy->name, "Unknown");
-
+      
       /* Fill in the joystick information fields. */
       {
          int num_axes = 0;
-- 
1.7.10.4


From 703000c28dec5fd577799255fcb312f8c53d70ac Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Wed, 26 Jun 2013 08:35:49 +0200
Subject: [PATCH 4/6] Now ex_joystick_events also displays the name of the
 joystick, and of the sticks, axes and buttons.

---
 examples/CMakeLists.txt       |    2 +-
 examples/ex_joystick_events.c |   43 ++++++++++++++++++++++++++++++++++++-----
 2 files changed, 39 insertions(+), 6 deletions(-)

diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index babde1a..a9d4389 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -135,7 +135,7 @@ example(ex_fs_resize ${IMAGE} ${PRIM})
 example(ex_fs_window ${IMAGE} ${PRIM} ${FONT})
 example(ex_icon ${IMAGE})
 example(ex_icon2 ${IMAGE})
-example(ex_joystick_events ${PRIM})
+example(ex_joystick_events ${PRIM} ${FONT})
 example(ex_joystick_hotplugging ${PRIM})
 example(ex_keyboard_events)
 example(ex_keyboard_focus)
diff --git a/examples/ex_joystick_events.c b/examples/ex_joystick_events.c
index e41ab84..1859a3f 100644
--- a/examples/ex_joystick_events.c
+++ b/examples/ex_joystick_events.c
@@ -6,8 +6,10 @@
 
 #include <allegro5/allegro.h>
 #include <allegro5/allegro_primitives.h>
+#include <allegro5/allegro_font.h>
 
 #include "common.c"
+#include <allegro5/joystick.h>
 
 #define MAX_AXES     3
 #define MAX_STICKS   16
@@ -19,12 +21,18 @@ ALLEGRO_EVENT_QUEUE  *event_queue;
 ALLEGRO_COLOR        black;
 ALLEGRO_COLOR        grey;
 ALLEGRO_COLOR        white;
+ALLEGRO_FONT         * font;   
 
 int num_sticks = 0;
 int num_buttons = 0;
 int num_axes[MAX_STICKS] = { 0 };
 float joys[MAX_STICKS][MAX_AXES] = {{ 0 }};
 bool joys_buttons[MAX_BUTTONS] = { 0 };
+const char * joy_name          = NULL;
+
+const char * joy_sticks_name[MAX_STICKS]         = { 0 };
+const char * joy_axes_name[MAX_STICKS][MAX_AXES] = {{ 0 }};
+const char * joy_buttons_name[MAX_BUTTONS]       = { 0 };
 
 
 static void setup_joystick_values(ALLEGRO_JOYSTICK *joy)
@@ -44,9 +52,13 @@ static void setup_joystick_values(ALLEGRO_JOYSTICK *joy)
    if (num_sticks > MAX_STICKS)
       num_sticks = MAX_STICKS;
    for (i = 0; i < num_sticks; i++) {
-      num_axes[i] = al_get_joystick_num_axes(joy, i);
-      for (j = 0; j < num_axes[i]; ++j)
+      num_axes[i] = al_get_joystick_num_axes(joy, i);      
+      joy_sticks_name[i] = al_get_joystick_stick_name(joy, i);
+      
+      for (j = 0; j < num_axes[i]; ++j) {
          joys[i][j] = jst.stick[i].axis[j];
+         joy_axes_name[i][j] = al_get_joystick_axis_name(joy, i, j);
+      }
    }
 
    num_buttons = al_get_joystick_num_buttons(joy);
@@ -55,7 +67,11 @@ static void setup_joystick_values(ALLEGRO_JOYSTICK *joy)
    }
    for (i = 0; i < num_buttons; i++) {
       joys_buttons[i] = (jst.button[i] >= 16384);
+      joy_buttons_name[i] = al_get_joystick_button_name(joy, i);
    }
+   
+   joy_name = al_get_joystick_name(joy);
+   
 }
 
 
@@ -73,11 +89,17 @@ static void draw_joystick_axes(int cx, int cy, int stick)
    al_draw_rectangle(cx-osize+0.5, cy-osize+0.5, cx+osize-0.5, cy+osize-0.5, black, 0);
    al_draw_filled_rectangle(x-5, y-5, x+5, y+5, black);
 
-   if (num_axes[stick] == 3) {
+   if (num_axes[stick] >= 3) {
       al_draw_filled_rectangle(zx-csize, cy-osize, zx+csize, cy+osize, grey);
       al_draw_rectangle(zx-csize+0.5f, cy-osize+0.5f, zx+csize-0.5f, cy+osize-0.5f, black, 0);
       al_draw_filled_rectangle(zx-5, z-5, zx+5, z+5, black);
+      al_draw_textf(font, black, cx , cy + osize,  ALLEGRO_ALIGN_CENTRE, "%s %s %s", joy_axes_name[stick][0], joy_axes_name[stick][1], joy_axes_name[stick][2]);
+   } else if (num_axes[stick] == 2) {
+      al_draw_textf(font, black, cx , cy + osize,  ALLEGRO_ALIGN_CENTRE, "%s %s", joy_axes_name[stick][0], joy_axes_name[stick][1]);
+   } else if (num_axes[stick] == 1) {
+      al_draw_textf(font, black, cx , cy + osize,  ALLEGRO_ALIGN_CENTRE, "%s", joy_axes_name[stick][0]);
    }
+   
 }
 
 
@@ -92,7 +114,11 @@ static void draw_joystick_button(int button, bool down)
    al_draw_rectangle(x+0.5, y+0.5, x + 24.5, y + 24.5, black, 0);
    if (down) {
       al_draw_filled_rectangle(x + 2, y + 2, x + 23, y + 23, black);
-   }
+      al_draw_textf(font, white, x + 13 , y + 8,  ALLEGRO_ALIGN_CENTRE, "%s", joy_buttons_name[button]);
+   } else {
+     al_draw_textf(font, black, x + 13, y + 8, ALLEGRO_ALIGN_CENTRE, "%s", joy_buttons_name[button]);
+  }
+   
 }
 
 
@@ -105,6 +131,9 @@ static void draw_all(void)
    int i;
 
    al_clear_to_color(al_map_rgb(0xff, 0xff, 0xc0));
+   
+   al_draw_textf(font, black, width / 2, height - 10,  ALLEGRO_ALIGN_CENTRE, "Joystick: %s", joy_name);
+   
 
    for (i = 0; i < num_sticks; i++) {
       int u = i%4;
@@ -188,6 +217,7 @@ int main(void)
       abort_example("Could not init Allegro.\n");
    }
    al_init_primitives_addon();
+   al_init_font_addon();
 
    display = al_create_display(640, 480);
    if (!display) {
@@ -197,8 +227,9 @@ int main(void)
    al_install_keyboard();
 
    black = al_map_rgb(0, 0, 0);
-   grey = al_map_rgb(0xe0, 0xe0, 0xe0);
+   grey  = al_map_rgb(0xe0, 0xe0, 0xe0);
    white = al_map_rgb(255, 255, 255);
+   font  = al_create_builtin_font();
 
    al_install_joystick();
 
@@ -216,6 +247,8 @@ int main(void)
    setup_joystick_values(al_get_joystick(0));
 
    main_loop();
+   
+   al_destroy_font(font);
 
    return 0;
 }
-- 
1.7.10.4


From 8e9d52a025b39ba8e2d7dfd6410759c4cb08a0ed Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Wed, 26 Jun 2013 08:47:16 +0200
Subject: [PATCH 5/6] Now ex_joystick_events also displays the name of the
 joystick, and of the sticks, axes and buttons.

---
 examples/ex_joystick_events.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/examples/ex_joystick_events.c b/examples/ex_joystick_events.c
index 1859a3f..50e0856 100644
--- a/examples/ex_joystick_events.c
+++ b/examples/ex_joystick_events.c
@@ -93,11 +93,11 @@ static void draw_joystick_axes(int cx, int cy, int stick)
       al_draw_filled_rectangle(zx-csize, cy-osize, zx+csize, cy+osize, grey);
       al_draw_rectangle(zx-csize+0.5f, cy-osize+0.5f, zx+csize-0.5f, cy+osize-0.5f, black, 0);
       al_draw_filled_rectangle(zx-5, z-5, zx+5, z+5, black);
-      al_draw_textf(font, black, cx , cy + osize,  ALLEGRO_ALIGN_CENTRE, "%s %s %s", joy_axes_name[stick][0], joy_axes_name[stick][1], joy_axes_name[stick][2]);
+      al_draw_textf(font, black, cx , cy + osize + 1,  ALLEGRO_ALIGN_CENTRE, "%s: %s %s %s", joy_sticks_name[stick], joy_axes_name[stick][0], joy_axes_name[stick][1], joy_axes_name[stick][2]);
    } else if (num_axes[stick] == 2) {
-      al_draw_textf(font, black, cx , cy + osize,  ALLEGRO_ALIGN_CENTRE, "%s %s", joy_axes_name[stick][0], joy_axes_name[stick][1]);
+      al_draw_textf(font, black, cx , cy + osize + 1,  ALLEGRO_ALIGN_CENTRE, "%s: %s %s", joy_sticks_name[stick], joy_axes_name[stick][0], joy_axes_name[stick][1]);
    } else if (num_axes[stick] == 1) {
-      al_draw_textf(font, black, cx , cy + osize,  ALLEGRO_ALIGN_CENTRE, "%s", joy_axes_name[stick][0]);
+      al_draw_textf(font, black, cx , cy + osize + 1,  ALLEGRO_ALIGN_CENTRE, "%s: %s", joy_sticks_name[stick], joy_axes_name[stick][0]);
    }
    
 }
-- 
1.7.10.4


From e1fab5abce293fbd72485575dbcb9976e735bfd5 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Wed, 26 Jun 2013 16:24:31 +0200
Subject: [PATCH 6/6] Correct stick detection range. Better names for sticks,
 throttles and throttle axes

---
 src/linux/ljoynu.c |   18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/src/linux/ljoynu.c b/src/linux/ljoynu.c
index 85adf5b..3df5bd5 100644
--- a/src/linux/ljoynu.c
+++ b/src/linux/ljoynu.c
@@ -385,7 +385,8 @@ static void ljoy_scan(bool configure)
          int res, i;  
          int stick = 0;
          int axis  = 0;
-         
+         int num_sticks    = 0;
+         int num_throttles = 0;
    
          // get_num_axes(fd, &num_axes);
          get_num_buttons(fd, &num_buttons);
@@ -396,7 +397,7 @@ static void ljoy_scan(bool configure)
          /* Scan the axes to get their properties. */
         res = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits);    
         if (res < 0) continue;
-        for (i = 0; i <= ABS_MISC; i++) {
+        for (i = ABS_X; i <= ABS_HAT3Y; i++) {
           if(TEST_BIT(i, abs_bits)) {
           struct input_absinfo absinfo;
           if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0)
@@ -407,12 +408,12 @@ static void ljoy_scan(bool configure)
               || (i == ABS_PRESSURE) || (i == ABS_DISTANCE) || (i== ABS_TOOL_WIDTH) 
             ) { 
               /* One axis throttle. */
+               num_throttles++;
                joy->parent.info.stick[stick].flags = ALLEGRO_JOYFLAG_ANALOGUE;
                joy->parent.info.stick[stick].num_axes = 1;
-               joy->parent.info.stick[stick].axis[0].name = "Throttle";
-               char *name = joy->parent.info.stick[stick].axis[0].name;
-               joy->parent.info.stick[stick].name = al_malloc(strlen(name) + 1);
-               strcpy(joy->parent.info.stick[stick].name, name);
+               joy->parent.info.stick[stick].axis[0].name = "X";
+               joy->parent.info.stick[stick].name = al_malloc(32);
+               snprintf((char *)joy->parent.info.stick[stick].name, 32, "Throttle %d", num_throttles);
                joy->axis_mapping[i].stick = stick;
                joy->axis_mapping[i].axis  = 0;
                joy->axis_mapping[i].min   = absinfo.minimum;
@@ -423,8 +424,9 @@ static void ljoy_scan(bool configure)
               /* Consider all these types of axis as throttle axis. */
                stick++;
             } else { /* regular axis, two axis stick. */
-              int digital = ((i >= ABS_HAT0X) && (i <=  ABS_HAT3Y));
+               int digital = ((i >= ABS_HAT0X) && (i <=  ABS_HAT3Y));
                if (axis == 0) { /* first axis of new joystick */
+                num_sticks++;
                 if (digital) {
                   joy->parent.info.stick[stick].flags = ALLEGRO_JOYFLAG_DIGITAL;
                 } else { 
@@ -434,7 +436,7 @@ static void ljoy_scan(bool configure)
                 joy->parent.info.stick[stick].axis[0].name = "X";
                 joy->parent.info.stick[stick].axis[1].name = "Y";
                 joy->parent.info.stick[stick].name = al_malloc (32);
-                snprintf((char *)joy->parent.info.stick[stick].name, 32, "Stick %d", stick+1);
+                snprintf((char *)joy->parent.info.stick[stick].name, 32, "Stick %d", num_sticks);
                 joy->axis_mapping[i].stick = stick;
                 joy->axis_mapping[i].axis  = axis;
                 joy->axis_mapping[i].min   = absinfo.minimum;
-- 
1.7.10.4

From 9dd6b7cebcbd272667b4678e20a1a97bfc8b2885 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Thu, 6 Jun 2013 20:31:16 +0200
Subject: [PATCH 1/7] Haptics or force feedback. A lot of boilerplate and some
 beginnings of a linux driver...

---
 cmake/FileList.cmake                       |    6 +-
 examples/CMakeLists.txt                    |    1 +
 examples/ex_haptic.c                       |   64 ++++
 include/allegro5/haptic.h                  |  183 ++++++++++++
 include/allegro5/internal/aintern_haptic.h |   87 ++++++
 include/allegro5/internal/aintern_system.h |    2 +
 include/allegro5/platform/aintunix.h       |    9 +
 lib/Headers/allegro5/haptic.h              |  183 ++++++++++++
 src/haptic.c                               |  239 +++++++++++++++
 src/linux/lhaptic.c                        |  439 ++++++++++++++++++++++++++++
 src/x/xsystem.c                            |    9 +
 11 files changed, 1221 insertions(+), 1 deletion(-)
 create mode 100644 examples/ex_haptic.c
 create mode 100644 include/allegro5/haptic.h
 create mode 100644 include/allegro5/internal/aintern_haptic.h
 create mode 100644 lib/Headers/allegro5/haptic.h
 create mode 100644 src/haptic.c
 create mode 100644 src/linux/lhaptic.c
 create mode 100644 src/macosx/ohaptic.m
 create mode 100644 src/win/whaptic.c

diff --git a/cmake/FileList.cmake b/cmake/FileList.cmake
index 3a33c50..12754f6 100644
--- a/cmake/FileList.cmake
+++ b/cmake/FileList.cmake
@@ -23,6 +23,7 @@ set(ALLEGRO_SRC_FILES
     src/fshook.c
     src/fshook_stdio.c
     src/fullscreen_mode.c
+    src/haptic.c
     src/inline.c
     src/joynu.c
     src/keybdnu.c
@@ -112,7 +113,8 @@ set(ALLEGRO_SRC_X_FILES
     src/x/xmousenu.c
     src/x/xrandr.c
     src/x/xsystem.c
-    src/x/xwindow.c
+    src/x/xwindow.c    
+    src/linux/lhaptic.c
     src/linux/ljoynu.c
     )
 
@@ -164,6 +166,7 @@ set(ALLEGRO_SRC_RASPBERRYPI_FILES
    src/linux/lkeybdnu.c
    src/linux/lmseev.c
    src/linux/lmsedrv.c
+   src/linux/lhaptic.c
    src/linux/ljoynu.c
    src/x/xevents.c
    src/x/xkeyboard.c
@@ -196,6 +199,7 @@ set(ALLEGRO_INCLUDE_ALLEGRO_FILES
     include/allegro5/fmaths.h
     include/allegro5/fshook.h
     include/allegro5/fullscreen_mode.h
+    include/allegro5/haptic.h
     include/allegro5/joystick.h
     include/allegro5/keyboard.h
     include/allegro5/keycodes.h
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index a9d4389..f45c0da 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -135,6 +135,7 @@ example(ex_fs_resize ${IMAGE} ${PRIM})
 example(ex_fs_window ${IMAGE} ${PRIM} ${FONT})
 example(ex_icon ${IMAGE})
 example(ex_icon2 ${IMAGE})
+example(ex_haptic ${PRIM})
 example(ex_joystick_events ${PRIM} ${FONT})
 example(ex_joystick_hotplugging ${PRIM})
 example(ex_keyboard_events)
diff --git a/examples/ex_haptic.c b/examples/ex_haptic.c
new file mode 100644
index 0000000..efaa0bf
--- /dev/null
+++ b/examples/ex_haptic.c
@@ -0,0 +1,64 @@
+/*
+ *    Example program for the Allegro library, by Peter Wang.
+ *
+ *    This program tests joystick events.
+ */
+
+#include <allegro5/allegro.h>
+#include <allegro5/haptic.h>
+#include <allegro5/allegro_primitives.h>
+
+#include "common.c"
+
+#define MAX_HAPTICS  32
+
+/* globals */
+ALLEGRO_EVENT_QUEUE  *event_queue;
+
+int num_haptics = 0;
+ALLEGRO_HAPTIC * haptics[MAX_HAPTICS];
+
+
+int main(void)
+{
+   int index;
+   ALLEGRO_DISPLAY *display;
+
+   if (!al_init()) {
+      abort_example("Could not init Allegro.\n");
+   }
+
+   display = al_create_display(640, 480);
+   if (!display) {
+      abort_example("al_create_display failed\n");
+   }
+
+   al_install_haptic();
+
+   event_queue = al_create_event_queue();
+   if (!event_queue) {
+      abort_example("al_create_event_queue failed\n");
+   }
+   open_log();
+   num_haptics = al_get_num_haptics();
+   log_printf("Found %d haptic devices.\n", num_haptics);
+   for(index = 0; index < num_haptics; index++) {
+     ALLEGRO_HAPTIC * hap = al_get_haptic(index);; 
+     haptics[index] = hap;
+     if (hap) {  
+      log_printf("Opened device %d: %s.\n", al_get_haptic_name(hap));
+     } else {
+      log_printf("Could not open haptic device %d.\n", index);
+     }
+   }
+   
+   
+   close_log(true);
+   // al_register_event_source(event_queue, al_get_display_event_source(display));
+   // al_register_event_source(event_queue, al_get_haptic_event_source());
+
+   
+   return 0;
+}
+
+/* vim: set ts=8 sts=3 sw=3 et: */
diff --git a/include/allegro5/haptic.h b/include/allegro5/haptic.h
new file mode 100644
index 0000000..3da4415
--- /dev/null
+++ b/include/allegro5/haptic.h
@@ -0,0 +1,183 @@
+/*         ______   ___    ___
+ *        /\  _  \ /\_ \  /\_ \
+ *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
+ *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
+ *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
+ *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
+ *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
+ *                                           /\____/
+ *                                           \_/__/
+ *
+ *      Haptic (that is, force feedback) routines for Allegro. 
+ *      By Beoran (beoran@xxxxxxxxx), 2013.
+ *
+ *      See readme.txt for copyright information.
+ */
+
+#ifndef __al_included_allegro5_haptic_h
+#define __al_included_allegro5_haptic_h
+
+#include "allegro5/base.h"
+#include "allegro5/events.h"
+#include "allegro5/mouse.h"
+#include "allegro5/joystick.h"
+
+#ifdef __cplusplus
+   extern "C" {
+#endif
+
+/* Enum: ALLEGRO_HAPTIC_FLAGS
+ */
+enum ALLEGRO_HAPTIC_FLAGS { 
+  ALLEGRO_HAPTIC_RUMBLE       = 1 << 0,
+  ALLEGRO_HAPTIC_PERIODIC     = 1 << 1,
+  ALLEGRO_HAPTIC_CONSTANT     = 1 << 2,
+  ALLEGRO_HAPTIC_SPRING       = 1 << 3,
+  ALLEGRO_HAPTIC_FRICTION     = 1 << 4,
+  ALLEGRO_HAPTIC_DAMPER       = 1 << 5,
+  ALLEGRO_HAPTIC_INERTIA      = 1 << 6,
+  ALLEGRO_HAPTIC_RAMP         = 1 << 7,
+  ALLEGRO_HAPTIC_SQUARE       = 1 << 8,
+  ALLEGRO_HAPTIC_TRIANGLE     = 1 << 9,
+  ALLEGRO_HAPTIC_SINE         = 1 << 10,
+  ALLEGRO_HAPTIC_SAW_UP       = 1 << 11,
+  ALLEGRO_HAPTIC_SAW_DOWN     = 1 << 12,
+  ALLEGRO_HAPTIC_CUSTOM       = 1 << 13,  
+  ALLEGRO_HAPTIC_GAIN         = 1 << 14,  
+  ALLEGRO_HAPTIC_AUTOCENTER   = 1 << 15,  
+};
+
+
+/* Type: ALLEGRO_HAPTIC
+ */
+typedef struct ALLEGRO_HAPTIC ALLEGRO_HAPTIC;
+
+/* Direction of a haptic effect. Angle is a value between 0 and 2*M_PI.
+ * An angle 0 means oriented towards the user, M_PI is away from the user 
+ * (towards the screen). 
+ * Radius (if supported ) is the diistance of the effect from the user 
+ * as a value between 0 and 1. Normally it is 0. 
+ * Azimuth is the angle of elevation, between -M_PI and M_PI. 0 points to the 
+ * horizontal plane, -M_PI points down, and M_PI points up.
+ * 
+ */
+struct ALLEGRO_HAPTIC_DIRECTION {
+  double angle; 
+  double radius;
+  double azimuth;
+};
+
+struct ALLEGRO_HAPTIC_REPLAY {
+    double length;
+    double delay;
+};
+
+struct ALLEGRO_HAPTIC_ENVELOPE {
+    double attack_length;
+    double attack_level;
+    double fade_length;
+    double fade_level;
+};
+
+struct ALLEGRO_HAPTIC_CONSTANT_EFFECT {
+    double level;
+    struct ALLEGRO_HAPTIC_ENVELOPE envelope;
+};
+
+struct ALLEGRO_HAPTIC_RAMP_EFFECT {
+    double start_level;
+    double end_level;
+    struct ALLEGRO_HAPTIC_ENVELOPE envelope;
+};
+
+struct ALLEGRO_HAPTIC_CONDITION_EFFECT {
+    double right_saturation;
+    double left_saturation;
+    double right_coeff;
+    double left_coeff;
+    double deadband;
+    double center;
+};
+
+struct ALLEGRO_HAPTIC_PERIODIC_EFFECT {
+    int waveform;
+    double period;
+    double magnitude;
+    double offset;
+    double phase;
+    
+    struct ALLEGRO_HAPTIC_ENVELOPE envelope;
+    int    custom_len;
+    double *custom_data;
+};
+
+struct ALLEGRO_HAPTIC_RUMBLE_EFFECT {
+    double strong_magnitude;
+    double weak_magnitude;
+};
+
+union ALLEGRO_HAPTIC_EFFECT_UNION {
+    struct ALLEGRO_HAPTIC_CONSTANT_EFFECT   constant;
+    struct ALLEGRO_HAPTIC_RAMP_EFFECT       ramp;
+    struct ALLEGRO_HAPTIC_PERIODIC_EFFECT   periodic;
+    struct ALLEGRO_HAPTIC_CONDITION_EFFECT  condition; 
+    struct ALLEGRO_HAPTIC_RUMBLE_EFFECT     rumble;
+};
+
+/* Type: ALLEGRO_HAPTIC_EFFECT
+ */
+struct ALLEGRO_HAPTIC_EFFECT {
+        int                                type;
+        int                                id;
+        struct ALLEGRO_HAPTIC_DIRECTION    direction;
+        struct ALLEGRO_HAPTIC_REPLAY       replay;
+        union ALLEGRO_HAPTIC_EFFECT_UNION  data; 
+};
+
+
+/* Type: ALLEGRO_HAPTIC_EFFECT
+ */
+typedef struct ALLEGRO_HAPTIC_EFFECT ALLEGRO_HAPTIC_EFFECT;
+
+
+
+
+AL_FUNC(bool,             al_install_haptic          , (void));
+AL_FUNC(void,             al_uninstall_haptic        , (void));
+AL_FUNC(bool,             al_is_haptic_installed     , (void));
+AL_FUNC(bool,             al_reconfigure_haptic      , (void));
+
+AL_FUNC(int,              al_get_num_haptics         , (void));
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic              , (int hapticn));
+AL_FUNC(void,             al_release_haptic          , (ALLEGRO_HAPTIC *));
+AL_FUNC(bool,             al_get_haptic_active       , (ALLEGRO_HAPTIC *));
+AL_FUNC(const char*,      al_get_haptic_name         , (ALLEGRO_HAPTIC *));
+
+AL_FUNC(int,              al_get_haptic_flags        , (ALLEGRO_HAPTIC *)); 
+
+AL_FUNC(bool,             al_is_mouse_haptic         , (ALLEGRO_MOUSE *));
+AL_FUNC(bool,             al_is_joystick_haptic      , (ALLEGRO_JOYSTICK *));
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_mouse   , (ALLEGRO_MOUSE *));
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_joystick, (ALLEGRO_JOYSTICK *));
+AL_FUNC(int,              al_get_haptic_num_axes     , (ALLEGRO_HAPTIC *)); 
+AL_FUNC(bool,             al_is_haptic_effect_ok     , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
+
+AL_FUNC(bool,             al_upload_haptic_effect    , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, int * play_id));
+AL_FUNC(bool,             al_play_haptic_effect      , (ALLEGRO_HAPTIC *, int play_id, int loop));
+AL_FUNC(bool,             al_stop_haptic_effect      , (ALLEGRO_HAPTIC *, int play_id));
+AL_FUNC(bool,             al_is_haptic_effect_stopped, (ALLEGRO_HAPTIC *, int play_id));
+AL_FUNC(bool,             al_stop_all_haptic_effects , (ALLEGRO_HAPTIC *, int play_id));
+AL_FUNC(bool,             al_is_haptic_effect_playing, (ALLEGRO_HAPTIC *, int play_id));
+AL_FUNC(bool,             al_rumble_haptic           , (ALLEGRO_HAPTIC *, double intensity, double duration, int * play_id));
+
+AL_FUNC(ALLEGRO_EVENT_SOURCE *, al_get_haptic_event_source, (void));
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/include/allegro5/internal/aintern_haptic.h b/include/allegro5/internal/aintern_haptic.h
new file mode 100644
index 0000000..f384c54
--- /dev/null
+++ b/include/allegro5/internal/aintern_haptic.h
@@ -0,0 +1,87 @@
+#ifndef __al_included_allegro5_aintern_haptic_h
+#define __al_included_allegro5_aintern_haptic_h
+
+#include "allegro5/haptic.h"
+
+#include "allegro5/internal/aintern_driver.h"
+#include "allegro5/internal/aintern_events.h"
+
+#ifdef __cplusplus
+   extern "C" {
+#endif
+
+/* Haptic devices driver virtual table.  */
+typedef struct ALLEGRO_HAPTIC_DRIVER
+{
+   int          hapdrv_id;
+   const char * hapdrv_name;
+   const char * hapdrv_desc;
+   const char * hapdrv_ascii_name;
+   AL_METHOD(bool, init_haptic, (void));
+   AL_METHOD(void, exit_haptic, (void));
+   AL_METHOD(bool, reconfigure_haptics, (void));
+   AL_METHOD(int, num_haptics, (void));
+   AL_METHOD(ALLEGRO_HAPTIC *, get_haptic, (int));
+   AL_METHOD(void, release_haptic    , (ALLEGRO_HAPTIC *));
+   AL_METHOD(int, get_haptic_flags   , (ALLEGRO_HAPTIC *));
+   AL_METHOD(const char *, get_name  , (ALLEGRO_HAPTIC *));
+   AL_METHOD(bool, get_active        , (ALLEGRO_HAPTIC *));
+   AL_METHOD(bool, is_mouse_haptic   , (ALLEGRO_MOUSE *));
+   AL_METHOD(bool, is_joystick_haptic, (ALLEGRO_JOYSTICK *));
+   AL_METHOD(ALLEGRO_HAPTIC *, get_from_mouse   , (ALLEGRO_MOUSE *));
+   AL_METHOD(ALLEGRO_HAPTIC *, get_from_joystick, (ALLEGRO_JOYSTICK *));
+   AL_METHOD(int , get_num_axes      , (ALLEGRO_HAPTIC *)); 
+   AL_METHOD(bool, is_effect_ok      , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
+   AL_METHOD(bool, upload_effect     , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, int *));
+   AL_METHOD(bool, play_effect       , (ALLEGRO_HAPTIC *, int, int ));
+   AL_METHOD(bool, stop_effect       , (ALLEGRO_HAPTIC *, int));
+   AL_METHOD(bool, is_effect_stopped , (ALLEGRO_HAPTIC *, int));
+   AL_METHOD(bool, stop_all_efects   , (ALLEGRO_HAPTIC *, int));
+   AL_METHOD(bool, is_effect_playing , (ALLEGRO_HAPTIC *, int));
+} ALLEGRO_HAPTIC_DRIVER;
+
+
+extern ALLEGRO_HAPTIC_DRIVER * _al_haptic_driver;
+
+
+
+/* macros for constructing the driver list */
+#define _AL_BEGIN_HAPTIC_DRIVER_LIST                         \
+   _AL_DRIVER_INFO _al_haptic_driver_list[] =                \
+   {
+
+#define _AL_END_HAPTIC_DRIVER_LIST                           \
+      {  0,                NULL,                false }      \
+   };
+
+#define _AL_HAPTIC_INFO_NAME_MAX            256
+   
+/* Can playback at most 32 haptic effects at the same time. */   
+#define _AL_HAPTIC_EFFECT_PLAYBACK_MAX      32    
+
+   
+/* information about an entire haptic */
+typedef struct _AL_HAPTIC_INFO
+{
+  int  id;
+  int  flags; 
+  int  num_axes;
+  char name[_AL_HAPTIC_INFO_NAME_MAX];
+} _AL_HAPTIC_INFO;
+
+
+
+struct ALLEGRO_HAPTIC
+{
+   _AL_HAPTIC_INFO info;
+};
+
+void _al_generate_haptic_event(ALLEGRO_EVENT *event);
+
+#ifdef __cplusplus
+   }
+#endif
+
+#endif
+
+/* vi ts=8 sts=3 sw=3 et */
diff --git a/include/allegro5/internal/aintern_system.h b/include/allegro5/internal/aintern_system.h
index 669a97c..ee54836 100644
--- a/include/allegro5/internal/aintern_system.h
+++ b/include/allegro5/internal/aintern_system.h
@@ -5,6 +5,7 @@
 #include "allegro5/internal/aintern_display.h"
 #include "allegro5/internal/aintern_dtor.h"
 #include "allegro5/internal/aintern_events.h"
+#include "allegro5/internal/aintern_haptic.h"
 #include "allegro5/internal/aintern_joystick.h"
 #include "allegro5/internal/aintern_keyboard.h"
 #include "allegro5/internal/aintern_mouse.h"
@@ -26,6 +27,7 @@ struct ALLEGRO_SYSTEM_INTERFACE
    ALLEGRO_MOUSE_DRIVER *(*get_mouse_driver)(void);
    ALLEGRO_TOUCH_INPUT_DRIVER *(*get_touch_input_driver)(void);
    ALLEGRO_JOYSTICK_DRIVER *(*get_joystick_driver)(void);
+   ALLEGRO_HAPTIC_DRIVER *(*get_haptic_driver)(void);   
    int (*get_num_display_modes)(void);
    ALLEGRO_DISPLAY_MODE *(*get_display_mode)(int index, ALLEGRO_DISPLAY_MODE *mode);
    void (*shutdown_system)(void);
diff --git a/include/allegro5/platform/aintunix.h b/include/allegro5/platform/aintunix.h
index d08cc72..2863df2 100644
--- a/include/allegro5/platform/aintunix.h
+++ b/include/allegro5/platform/aintunix.h
@@ -74,6 +74,15 @@ void _al_unix_stop_watching_fd(int fd);
 AL_VAR(struct ALLEGRO_JOYSTICK_DRIVER, _al_joydrv_linux);
 #endif
 
+/* lhaptic.c */
+/* This isn't in aintlnx.h because it's needed for the X11 port as well. */
+#define _ALLEGRO_HAPDRV_LINUX    AL_ID('L','N','X','H')
+
+#ifdef ALLEGRO_HAVE_LINUX_HAPTIC_H
+AL_VAR(struct ALLEGRO_HAPTIC_DRIVER, _al_hapdrv_linux);
+#endif
+
+
 #ifdef __cplusplus
    }
 #endif
diff --git a/lib/Headers/allegro5/haptic.h b/lib/Headers/allegro5/haptic.h
new file mode 100644
index 0000000..3da4415
--- /dev/null
+++ b/lib/Headers/allegro5/haptic.h
@@ -0,0 +1,183 @@
+/*         ______   ___    ___
+ *        /\  _  \ /\_ \  /\_ \
+ *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
+ *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
+ *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
+ *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
+ *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
+ *                                           /\____/
+ *                                           \_/__/
+ *
+ *      Haptic (that is, force feedback) routines for Allegro. 
+ *      By Beoran (beoran@xxxxxxxxx), 2013.
+ *
+ *      See readme.txt for copyright information.
+ */
+
+#ifndef __al_included_allegro5_haptic_h
+#define __al_included_allegro5_haptic_h
+
+#include "allegro5/base.h"
+#include "allegro5/events.h"
+#include "allegro5/mouse.h"
+#include "allegro5/joystick.h"
+
+#ifdef __cplusplus
+   extern "C" {
+#endif
+
+/* Enum: ALLEGRO_HAPTIC_FLAGS
+ */
+enum ALLEGRO_HAPTIC_FLAGS { 
+  ALLEGRO_HAPTIC_RUMBLE       = 1 << 0,
+  ALLEGRO_HAPTIC_PERIODIC     = 1 << 1,
+  ALLEGRO_HAPTIC_CONSTANT     = 1 << 2,
+  ALLEGRO_HAPTIC_SPRING       = 1 << 3,
+  ALLEGRO_HAPTIC_FRICTION     = 1 << 4,
+  ALLEGRO_HAPTIC_DAMPER       = 1 << 5,
+  ALLEGRO_HAPTIC_INERTIA      = 1 << 6,
+  ALLEGRO_HAPTIC_RAMP         = 1 << 7,
+  ALLEGRO_HAPTIC_SQUARE       = 1 << 8,
+  ALLEGRO_HAPTIC_TRIANGLE     = 1 << 9,
+  ALLEGRO_HAPTIC_SINE         = 1 << 10,
+  ALLEGRO_HAPTIC_SAW_UP       = 1 << 11,
+  ALLEGRO_HAPTIC_SAW_DOWN     = 1 << 12,
+  ALLEGRO_HAPTIC_CUSTOM       = 1 << 13,  
+  ALLEGRO_HAPTIC_GAIN         = 1 << 14,  
+  ALLEGRO_HAPTIC_AUTOCENTER   = 1 << 15,  
+};
+
+
+/* Type: ALLEGRO_HAPTIC
+ */
+typedef struct ALLEGRO_HAPTIC ALLEGRO_HAPTIC;
+
+/* Direction of a haptic effect. Angle is a value between 0 and 2*M_PI.
+ * An angle 0 means oriented towards the user, M_PI is away from the user 
+ * (towards the screen). 
+ * Radius (if supported ) is the diistance of the effect from the user 
+ * as a value between 0 and 1. Normally it is 0. 
+ * Azimuth is the angle of elevation, between -M_PI and M_PI. 0 points to the 
+ * horizontal plane, -M_PI points down, and M_PI points up.
+ * 
+ */
+struct ALLEGRO_HAPTIC_DIRECTION {
+  double angle; 
+  double radius;
+  double azimuth;
+};
+
+struct ALLEGRO_HAPTIC_REPLAY {
+    double length;
+    double delay;
+};
+
+struct ALLEGRO_HAPTIC_ENVELOPE {
+    double attack_length;
+    double attack_level;
+    double fade_length;
+    double fade_level;
+};
+
+struct ALLEGRO_HAPTIC_CONSTANT_EFFECT {
+    double level;
+    struct ALLEGRO_HAPTIC_ENVELOPE envelope;
+};
+
+struct ALLEGRO_HAPTIC_RAMP_EFFECT {
+    double start_level;
+    double end_level;
+    struct ALLEGRO_HAPTIC_ENVELOPE envelope;
+};
+
+struct ALLEGRO_HAPTIC_CONDITION_EFFECT {
+    double right_saturation;
+    double left_saturation;
+    double right_coeff;
+    double left_coeff;
+    double deadband;
+    double center;
+};
+
+struct ALLEGRO_HAPTIC_PERIODIC_EFFECT {
+    int waveform;
+    double period;
+    double magnitude;
+    double offset;
+    double phase;
+    
+    struct ALLEGRO_HAPTIC_ENVELOPE envelope;
+    int    custom_len;
+    double *custom_data;
+};
+
+struct ALLEGRO_HAPTIC_RUMBLE_EFFECT {
+    double strong_magnitude;
+    double weak_magnitude;
+};
+
+union ALLEGRO_HAPTIC_EFFECT_UNION {
+    struct ALLEGRO_HAPTIC_CONSTANT_EFFECT   constant;
+    struct ALLEGRO_HAPTIC_RAMP_EFFECT       ramp;
+    struct ALLEGRO_HAPTIC_PERIODIC_EFFECT   periodic;
+    struct ALLEGRO_HAPTIC_CONDITION_EFFECT  condition; 
+    struct ALLEGRO_HAPTIC_RUMBLE_EFFECT     rumble;
+};
+
+/* Type: ALLEGRO_HAPTIC_EFFECT
+ */
+struct ALLEGRO_HAPTIC_EFFECT {
+        int                                type;
+        int                                id;
+        struct ALLEGRO_HAPTIC_DIRECTION    direction;
+        struct ALLEGRO_HAPTIC_REPLAY       replay;
+        union ALLEGRO_HAPTIC_EFFECT_UNION  data; 
+};
+
+
+/* Type: ALLEGRO_HAPTIC_EFFECT
+ */
+typedef struct ALLEGRO_HAPTIC_EFFECT ALLEGRO_HAPTIC_EFFECT;
+
+
+
+
+AL_FUNC(bool,             al_install_haptic          , (void));
+AL_FUNC(void,             al_uninstall_haptic        , (void));
+AL_FUNC(bool,             al_is_haptic_installed     , (void));
+AL_FUNC(bool,             al_reconfigure_haptic      , (void));
+
+AL_FUNC(int,              al_get_num_haptics         , (void));
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic              , (int hapticn));
+AL_FUNC(void,             al_release_haptic          , (ALLEGRO_HAPTIC *));
+AL_FUNC(bool,             al_get_haptic_active       , (ALLEGRO_HAPTIC *));
+AL_FUNC(const char*,      al_get_haptic_name         , (ALLEGRO_HAPTIC *));
+
+AL_FUNC(int,              al_get_haptic_flags        , (ALLEGRO_HAPTIC *)); 
+
+AL_FUNC(bool,             al_is_mouse_haptic         , (ALLEGRO_MOUSE *));
+AL_FUNC(bool,             al_is_joystick_haptic      , (ALLEGRO_JOYSTICK *));
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_mouse   , (ALLEGRO_MOUSE *));
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_joystick, (ALLEGRO_JOYSTICK *));
+AL_FUNC(int,              al_get_haptic_num_axes     , (ALLEGRO_HAPTIC *)); 
+AL_FUNC(bool,             al_is_haptic_effect_ok     , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
+
+AL_FUNC(bool,             al_upload_haptic_effect    , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, int * play_id));
+AL_FUNC(bool,             al_play_haptic_effect      , (ALLEGRO_HAPTIC *, int play_id, int loop));
+AL_FUNC(bool,             al_stop_haptic_effect      , (ALLEGRO_HAPTIC *, int play_id));
+AL_FUNC(bool,             al_is_haptic_effect_stopped, (ALLEGRO_HAPTIC *, int play_id));
+AL_FUNC(bool,             al_stop_all_haptic_effects , (ALLEGRO_HAPTIC *, int play_id));
+AL_FUNC(bool,             al_is_haptic_effect_playing, (ALLEGRO_HAPTIC *, int play_id));
+AL_FUNC(bool,             al_rumble_haptic           , (ALLEGRO_HAPTIC *, double intensity, double duration, int * play_id));
+
+AL_FUNC(ALLEGRO_EVENT_SOURCE *, al_get_haptic_event_source, (void));
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/haptic.c b/src/haptic.c
new file mode 100644
index 0000000..7dcdc6c
--- /dev/null
+++ b/src/haptic.c
@@ -0,0 +1,239 @@
+/*         ______   ___    ___ 
+ *        /\  _  \ /\_ \  /\_ \ 
+ *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
+ *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
+ *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
+ *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
+ *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
+ *                                           /\____/
+ *                                           \_/__/
+ *
+ *      New haptic API.
+ * 
+ *      By Peter Wang.
+ *
+ *      See readme.txt for copyright information.
+ */
+
+/* Title: Joystick routines
+ */
+
+
+#define ALLEGRO_NO_COMPATIBILITY
+
+#include "allegro5/allegro.h"
+#include "allegro5/haptic.h"
+#include "allegro5/internal/aintern.h"
+#include "allegro5/internal/aintern_events.h"
+#include "allegro5/internal/aintern_exitfunc.h"
+#include "allegro5/internal/aintern_haptic.h"
+#include "allegro5/internal/aintern_system.h"
+
+
+
+/* the active haptic driver */
+static ALLEGRO_HAPTIC_DRIVER *haptic_driver = NULL;
+static ALLEGRO_EVENT_SOURCE   haptic_es;
+
+
+/* Function: al_install_haptic
+ */
+bool al_install_haptic(void)
+{
+   ALLEGRO_SYSTEM *sysdrv;
+   ALLEGRO_HAPTIC_DRIVER *hapdrv;
+
+   if (haptic_driver)
+      return true;
+
+   sysdrv = al_get_system_driver();
+   ASSERT(sysdrv);
+
+   /* Currently every platform only has at most one haptic driver. */
+   if (sysdrv->vt->get_haptic_driver) {
+      hapdrv = sysdrv->vt->get_haptic_driver();
+      /* Avoid race condition in case the haptic driver generates an
+       * event right after ->init_haptic.
+       */
+      _al_event_source_init(&haptic_es);
+      if (hapdrv && hapdrv->init_haptic()) {
+         haptic_driver = hapdrv;
+         _al_add_exit_func(al_uninstall_haptic, "al_uninstall_haptic");
+         return true;
+      }
+      _al_event_source_free(&haptic_es);
+   }
+
+   return false;
+}
+
+
+
+/* Function: al_uninstall_haptic
+ */
+void al_uninstall_haptic(void)
+{
+   if (haptic_driver) {
+      /* perform driver clean up */
+      haptic_driver->exit_haptic();
+      _al_event_source_free(&haptic_es);
+      haptic_driver = NULL;
+   }
+}
+
+
+/* Function: al_is_haptic_installed
+ */
+bool al_is_haptic_installed(void)
+{
+   return (haptic_driver) ? true : false;
+}
+
+
+/* Function: al_reconfigure_haptics
+ */
+bool al_reconfigure_haptics(void)
+{
+   if (!haptic_driver)
+      return false;
+   
+   return haptic_driver->reconfigure_haptics();
+}
+
+
+
+/* Function: al_get_haptic_event_source
+ */
+ALLEGRO_EVENT_SOURCE *al_get_haptic_event_source(void)
+{
+   if (!haptic_driver)
+      return NULL;
+   return &haptic_es;
+}
+
+
+
+void _al_generate_haptic_event(ALLEGRO_EVENT *event)
+{
+   ASSERT(haptic_driver);
+
+   _al_event_source_lock(&haptic_es);
+   if (_al_event_source_needs_to_generate_event(&haptic_es)) {
+      _al_event_source_emit_event(&haptic_es, event);
+   }
+   _al_event_source_unlock(&haptic_es);
+}
+
+
+
+/* Function: al_get_num_haptics
+ */
+int al_get_num_haptics(void)
+{
+   if (haptic_driver)
+      return haptic_driver->num_haptics();
+
+   return 0;
+}
+
+
+
+/* Function: al_get_haptic
+ */
+ALLEGRO_HAPTIC * al_get_haptic(int num)
+{
+   ASSERT(haptic_driver);
+   ASSERT(num >= 0);
+
+   return haptic_driver->get_haptic(num);
+}
+
+
+
+/* Function: al_release_haptic
+ */
+void al_release_haptic(ALLEGRO_HAPTIC *hap)
+{
+   ASSERT(haptic_driver);
+   ASSERT(hap);
+
+   haptic_driver->release_haptic(hap);
+}
+
+
+
+/* Function: al_get_haptic_active
+ */
+bool al_get_haptic_active(ALLEGRO_HAPTIC *hap)
+{
+   ASSERT(hap);
+
+   return hap->info.flags;
+}
+
+
+/* Function: al_get_haptic_num_axes
+ */
+int al_get_haptic_num_axes(ALLEGRO_HAPTIC *hap)
+{
+   ASSERT(hap);
+
+   return hap->info.num_axes;
+}
+
+
+/* Function: al_get_haptic_flags
+ */
+int al_get_haptic_flags(ALLEGRO_HAPTIC *hap)
+{
+   ASSERT(hap);
+
+   return haptic_driver->get_haptic_flags(hap);
+}
+
+
+
+/* Function: al_get_haptic_name
+ */
+const char *al_get_haptic_name(ALLEGRO_HAPTIC *hap)
+{
+   ASSERT(hap);
+   return haptic_driver->get_name(hap);
+   return NULL;
+}
+
+/*
+AL_FUNC(bool,             al_install_haptic          , (void));
+AL_FUNC(void,             al_uninstall_haptic        , (void));
+AL_FUNC(bool,             al_is_haptic_installed     , (void));
+AL_FUNC(bool,             al_reconfigure_haptic      , (void));
+
+AL_FUNC(int,              al_get_num_haptics         , (void));
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic              , (int hapticn));
+AL_FUNC(void,             al_release_haptic          , (ALLEGRO_HAPTIC *));
+AL_FUNC(bool,             al_get_haptic_active       , (ALLEGRO_HAPTIC *));
+AL_FUNC(const char*,      al_get_haptic_name         , (ALLEGRO_HAPTIC *));
+
+AL_FUNC(int,              al_get_haptic_flags        , (ALLEGRO_HAPTIC *)); 
+
+AL_FUNC(bool,             al_is_mouse_haptic         , (ALLEGRO_MOUSE *));
+AL_FUNC(bool,             al_is_joystick_haptic      , (ALLEGRO_JOYSTICK *));
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_mouse   , (ALLEGRO_MOUSE *));
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_joystick, (ALLEGRO_JOYSTICK *));
+AL_FUNC(int,              al_get_haptic_num_axes     , (ALLEGRO_HAPTIC *)); 
+AL_FUNC(bool,             al_is_haptic_effect_ok     , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
+
+AL_FUNC(bool,             al_upload_haptic_effect    , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, int * play_id));
+AL_FUNC(bool,             al_play_haptic_effect      , (ALLEGRO_HAPTIC *, int play_id, int loop));
+AL_FUNC(bool,             al_stop_haptic_effect      , (ALLEGRO_HAPTIC *, int play_id));
+AL_FUNC(bool,             al_is_haptic_effect_playing, (ALLEGRO_HAPTIC *, int play_id));
+AL_FUNC(bool,             al_stop_all_haptic_effects , (ALLEGRO_HAPTIC *, int play_id));
+AL_FUNC(bool,             al_is_haptic_stopped       , (ALLEGRO_HAPTIC *, int play_id));
+AL_FUNC(bool,             al_rumble_haptic           , (ALLEGRO_HAPTIC *, double intensity, double duration, int * play_id));
+
+
+*/
+
+
+
+
diff --git a/src/linux/lhaptic.c b/src/linux/lhaptic.c
new file mode 100644
index 0000000..3d0d69e
--- /dev/null
+++ b/src/linux/lhaptic.c
@@ -0,0 +1,439 @@
+/*         ______   ___    ___ 
+ *        /\  _  \ /\_ \  /\_ \ 
+ *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
+ *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
+ *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
+ *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
+ *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
+ *                                           /\____/
+ *                                           \_/__/
+ *
+ *      Linux haptic (force-feedback) device driver.
+ *
+ *      By Beoran.
+ * 
+ *      See readme.txt for copyright information.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <linux/input.h>
+#include <fcntl.h>      
+#include <limits.h>     
+#include <errno.h>      
+#include <math.h>
+#include <glob.h>
+
+
+#define ALLEGRO_NO_KEY_DEFINES
+#define ALLEGRO_NO_COMPATIBILITY
+
+#include "allegro5/allegro.h"
+#include "allegro5/haptic.h"
+#include "allegro5/path.h"
+#include "allegro5/platform/alplatf.h"
+#include "allegro5/internal/aintern.h"
+#include "allegro5/internal/aintern_events.h"
+#include "allegro5/internal/aintern_haptic.h"
+#include "allegro5/platform/aintunix.h"
+#include </home/bjorn/src/allegro/lib/Headers/allegro5/haptic.h>
+
+
+#if defined(ALLEGRO_HAVE_SYS_INOTIFY_H) && defined(ALLEGRO_HAVE_SYS_TIMERFD_H)
+   #define SUPPORT_HOTPLUG
+   #include <sys/inotify.h>
+   #include <sys/timerfd.h>
+#endif
+
+ALLEGRO_DEBUG_CHANNEL("lhaptic");
+
+/* Support at most 32 haptic devices. */
+#define HAPTICS_MAX             32
+/* Use dumb char buffers of 100 characters. whihc is "enough 
+ *for everyone" for now. :p */
+#define HAPTICS_BUF_MAX         1000
+
+
+typedef struct ALLEGRO_HAPTIC_LINUX
+{
+   int in_use;
+   ALLEGRO_HAPTIC parent;
+   int config_state;
+   bool marked;
+   int fd;
+   char device_name[HAPTICS_BUF_MAX];
+   int state;
+   char name[HAPTICS_BUF_MAX];
+   int flags;
+} ALLEGRO_HAPTIC_LINUX;
+
+
+
+static bool lhap_init_haptic(void);
+static void lhap_exit_haptic(void);
+
+static bool lhap_reconfigure_haptics(void);
+static int lhap_num_haptics(void);
+static ALLEGRO_HAPTIC *lhap_get_haptic(int num);
+static void lhap_release_haptic(ALLEGRO_HAPTIC *hap_);
+
+static int lhap_get_flags(ALLEGRO_HAPTIC *hap_);
+static const char *lhap_get_name(ALLEGRO_HAPTIC *hap_);
+
+
+/* forward declarations 
+static bool lhap_get_active(ALLEGRO_HAPTIC *hap_);
+static void lhap_generate_event(ALLEGRO_HAPTIC_LINUX *joy, int button, ALLEGRO_EVENT_TYPE event_type);
+*/
+
+static bool lhap_get_active(ALLEGRO_HAPTIC *);
+static bool lhap_is_mouse_haptic(ALLEGRO_MOUSE *);
+static bool lhap_is_joystick_haptic(ALLEGRO_JOYSTICK *);
+static ALLEGRO_HAPTIC *  lhap_get_from_mouse(ALLEGRO_MOUSE *);
+static ALLEGRO_HAPTIC *  lhap_get_from_joystick(ALLEGRO_JOYSTICK *);
+static int               lhap_get_num_axes(ALLEGRO_HAPTIC *); 
+static bool lhap_is_effect_ok(ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *);
+static bool lhap_upload_effect(ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, int *);
+static bool lhap_play_effect(ALLEGRO_HAPTIC *, int, int);
+static bool lhap_stop_effect(ALLEGRO_HAPTIC *, int);
+static bool lhap_is_effect_stopped(ALLEGRO_HAPTIC *, int);
+static bool lhap_stop_all_effects(ALLEGRO_HAPTIC *);
+static bool lhap_is_effect_playing(ALLEGRO_HAPTIC *, int);
+
+/* The haptics driver vtable. */
+ALLEGRO_HAPTIC_DRIVER hapdrv_linux = 
+{
+   _ALLEGRO_HAPDRV_LINUX,
+   "",
+   "",
+   "Linux haptic(s)",
+   lhap_init_haptic,
+   lhap_exit_haptic,
+   lhap_reconfigure_haptics,
+   lhap_num_haptics,
+   lhap_get_haptic,
+   lhap_release_haptic,
+   lhap_get_flags,
+   lhap_get_name,
+   lhap_get_active,
+   lhap_is_mouse_haptic,
+   lhap_is_joystick_haptic,
+   lhap_get_from_mouse,
+   lhap_get_from_joystick,
+   lhap_get_num_axes,
+   lhap_is_effect_ok,
+   lhap_upload_effect,
+   lhap_play_effect,
+   lhap_stop_effect,
+   lhap_is_effect_stopped,
+   lhap_stop_all_effects,
+   lhap_is_effect_playing
+};
+
+
+ALLEGRO_HAPTIC_DRIVER * _al_haptic_driver = &hapdrv_linux;
+
+
+
+ALLEGRO_HAPTIC_DRIVER *_al_linux_haptic_driver(void)
+{
+   return &hapdrv_linux;
+}
+
+
+
+static unsigned                 num_haptics = 0;  
+/* number of haptics known to the user */
+static ALLEGRO_HAPTIC_LINUX     haptics[HAPTICS_MAX];
+static ALLEGRO_MUTEX          * haptic_mutex;
+#ifdef SUPPORT_HOTPLUG
+
+static int                      haptic_inotify_fd = -1;
+static int                      haptic_timer_fd = -1;
+
+#endif
+
+/* Approach: to find the available haptic devices, scan 
+ * the /sys/class/input directory for any directory named eventxxx. 
+ * Then read  /sys/class/input/event1/capabilities/ff to find out the 
+ * capabilities of the device. If it's 0, then no it doesn't support force feedback.
+ * The idea of this approach is that the /dev file doesn' t have to be opened
+ * to inspect the existence of the haptic device. 
+ */
+
+/*
+ * Scans the /sys/class/input/ directory to find any haptic devices and 
+ * already sets them up to be used if needed. Returns the amount of haptics found
+ * or negative or error.
+ */
+int lhap_scan_haptics(void) {
+  char buf[HAPTICS_BUF_MAX];
+  char line[HAPTICS_BUF_MAX];
+  glob_t found;
+  int res;
+  unsigned int index;
+  num_haptics = 0;
+  for (index = 0; index < HAPTICS_MAX; index ++) {
+    haptics[index].in_use = 0;
+    haptics[index].fd     = -1;
+  }  
+  res         = glob("/sys/class/input/event*", GLOB_MARK, NULL, &found);
+  if (res == GLOB_NOMATCH) { 
+    globfree(&found);
+    return -ENOENT;
+  }
+  for (index = 0; index < found.gl_pathc; index ++) {
+    int scanres;
+    unsigned int capa = 0, extra = 0;
+    FILE * fin;
+    char * path = found.gl_pathv[index]; 
+    memset(buf, 0, HAPTICS_BUF_MAX);
+    snprintf(buf, HAPTICS_BUF_MAX, "%sdevice/capabilities/ff", path);
+    fin = fopen(buf, "r");
+    if(!fin) {  continue;  }
+    scanres = fscanf(fin, "%u%u", &capa, &extra);
+    fclose(fin);
+    
+    if (capa > 0) { 
+      char * devname = strchr(strchr(path + 1, '/') + 1, '/');
+      /* It's a haptic device. */
+      ALLEGRO_HAPTIC_LINUX * hap = haptics + num_haptics;
+      hap->parent.info.id        = num_haptics;
+      num_haptics++;
+      hap->flags     = capa;
+      snprintf(hap->device_name, HAPTICS_BUF_MAX, "/dev%s", devname);
+      hap->device_name[strlen(hap->device_name) -1] = '\0';
+      snprintf(buf, HAPTICS_BUF_MAX, "%sdevice/name", path);
+      fin = fopen(buf, "r");
+      if (!fin) {  continue;  }
+      if(fgets(line, HAPTICS_BUF_MAX, fin)) {
+        line[HAPTICS_BUF_MAX-1] = '\0';
+        line[strlen(line)]      = '\0';
+        strcpy(hap->name, line);      
+      }
+      fclose(fin);      
+      fprintf(stderr, "Haptic device found: %s (%s) (%s), %d %d %d\n", buf, hap->device_name, hap->name, scanres, capa, extra);
+      
+    }
+    
+  }
+  globfree(&found);  
+  return num_haptics;
+} 
+
+
+bool lhap_init_haptic(void) {
+  return lhap_scan_haptics() >= 0;
+}
+
+
+void lhap_exit_haptic() {
+   return;
+}
+
+static bool lhap_reconfigure_haptics(void){
+  return 0;
+}
+
+int lhap_num_haptics() {
+  return num_haptics;
+}
+
+ALLEGRO_HAPTIC_LINUX * lhap_al2lin(ALLEGRO_HAPTIC * haptic) {
+  if(!haptic) return NULL;
+  ALLEGRO_HAPTIC_LINUX  * lhap = haptics + haptic->info.id;
+  /* Could also have used offsetof, but, hey... */
+  return lhap;
+}
+
+
+void lhap_release_haptic(ALLEGRO_HAPTIC * haptic) {
+  ALLEGRO_HAPTIC_LINUX  * lhap = lhap_al2lin(haptic);
+  ASSERT(haptic);
+  if (!lhap->in_use) return;
+  if (lhap->fd < 0) return;
+  close(lhap->fd);
+}
+
+
+ALLEGRO_HAPTIC * lhap_get_haptic(int index) {
+  ALLEGRO_HAPTIC_LINUX * lhap;
+  if(index >= HAPTICS_MAX) return NULL;
+  lhap = haptics + index;
+  if (!lhap->in_use) {
+    lhap->fd     = open(lhap->device_name, O_RDWR);
+    if(lhap->fd < 0 ) return NULL;
+    lhap->in_use = 1;
+    return &lhap->parent;
+  } else {
+    return &lhap->parent;
+  }
+  return NULL;
+}
+
+
+const char * lhap_get_name(ALLEGRO_HAPTIC * haptic) {
+  ALLEGRO_HAPTIC_LINUX * lhap = lhap_al2lin(haptic);
+  return lhap->name;
+}
+
+int lhap_get_flags(ALLEGRO_HAPTIC * haptic) {
+  ALLEGRO_HAPTIC_LINUX * lhap = lhap_al2lin(haptic);
+  return lhap->flags;
+}
+
+
+static bool lhap_type2lin(__u16 * res, int type) {
+  ASSERT(res);
+  
+  switch (type) {
+    case ALLEGRO_HAPTIC_RUMBLE          : (*res) = FF_RUMBLE       ; break;
+    case ALLEGRO_HAPTIC_PERIODIC        : (*res) = FF_PERIODIC     ; break;
+    case ALLEGRO_HAPTIC_CONSTANT        : (*res) = FF_CONSTANT     ; break;
+    case ALLEGRO_HAPTIC_SPRING          : (*res) = FF_SPRING       ; break;
+    case ALLEGRO_HAPTIC_FRICTION        : (*res) = FF_FRICTION     ; break;
+    case ALLEGRO_HAPTIC_DAMPER          : (*res) = FF_DAMPER       ; break;
+    case ALLEGRO_HAPTIC_INERTIA         : (*res) = FF_INERTIA      ; break;
+    case ALLEGRO_HAPTIC_RAMP            : (*res) = FF_RAMP         ; break;
+    default: 
+      return false;
+  }
+  return true;
+}
+
+static bool lhap_wave2lin(__u16 * res, int type) {
+  ASSERT(res);
+  
+  switch (type) {
+    case ALLEGRO_HAPTIC_SQUARE          : (*res) = FF_SQUARE       ; break;
+    case ALLEGRO_HAPTIC_TRIANGLE        : (*res) = FF_TRIANGLE     ; break;
+    case ALLEGRO_HAPTIC_SINE            : (*res) = FF_SINE         ; break;
+    case ALLEGRO_HAPTIC_SAW_UP          : (*res) = FF_SAW_UP       ; break;
+    case ALLEGRO_HAPTIC_SAW_DOWN        : (*res) = FF_SAW_DOWN     ; break;
+    case ALLEGRO_HAPTIC_CUSTOM          : (*res) = FF_CUSTOM       ; break;    
+    default: 
+      return false;
+  }
+  return true;
+}
+
+/* converts the time in seconds to a linux compatible time. Return false if
+ out of bounds. */
+static bool lhap_time2lin(__u16 * res, double sec) {
+  ASSERT(res); 
+  
+  if (sec < 0.0)        return false; 
+  if (sec > 32.767)     return false;
+  return (__u16) round(sec * 1000.0);
+}
+
+/* Converts replay data to linux. */
+static bool lhap_replay2lin(struct ff_replay * lin, ALLEGRO_HAPTIC_REPLAY * al) {
+  if(!lhap_time2lin(&lin.delay, al.delay))  return false;
+  if(!lhap_time2lin(&lin.length, al.length)) return false;
+  return true;
+}
+
+/* Converts the level in range 0.0 to 1.0 to a linux compatible level. 
+ * Returns false if out of bounds. */
+static bool lhap_level2lin(__u16 * res, double level) {
+  ASSERT(res);   
+  if (level < 0.0)        return false; 
+  if (level > 1.0)        return false;
+  return (__u16) round(level * ((double) 0x7fff));
+}
+
+/* Converts a rumble effect to linux. */
+static bool lhap_rumble2lin(struct ff_rumble_effect * lin, ALLEGRO_HAPTIC_RUMBLE_EFFECT * al) {
+  if(!lhap_level2lin(&lin->strong_magnitude, al->strong_magnitude)) return false;
+  if(!lhap_level2lin(&lin->weak_magnitude  , al->weak_magnitude)) return false;
+  return true;
+}
+
+/* converts a periodic effect  to linux */
+static bool lhap_periodic2lin(struct ff_periodic_effect * lin, ALLEGRO_HAPTIC_PERIODIC_EFFECT * al) {
+  
+  return true;
+}
+
+/* Converts allegro haptic effect to linux haptic effect. */
+static bool lhap_effect2lin(struct ff_effect * lin, ALLEGRO_HAPTIC_EFFECT * al) {
+  if(!lhap_type2lin(&lin->type, al->type,) return false;
+  /* lin_effect->replay = effect->re; */
+  lin->direction = (__u16) round(((double)0xC000 * al->direction.angle) / (2 * M_PI));
+  lin->id        = -1;
+  if(!lhap_replay2lin(&lin->replay, &al->replay);
+  switch(lin->type) {
+    case FF_RUMBLE:
+      if(!lhap_rumble2lin(&lin->rumble, &al->rumble);
+      break;
+    case FF_PERIODIC:      
+      if(!lhap_periodic2lin(&lin->periodic, al->data.periodic)) return false;
+      break;
+      
+  }
+     
+  return false;
+}
+
+
+
+static bool lhap_get_active(ALLEGRO_HAPTIC * haptic) {
+  ALLEGRO_HAPTIC_LINUX * lhap = lhap_al2lin(haptic);
+  return lhap->in_use;
+}
+
+static bool lhap_is_mouse_haptic(ALLEGRO_MOUSE * mouse) {
+  return false;
+}
+
+static bool lhap_is_joystick_haptic(ALLEGRO_JOYSTICK * joy) {
+   return false;
+}
+
+static ALLEGRO_HAPTIC *  lhap_get_from_mouse(ALLEGRO_MOUSE * mouse) {
+  return NULL;
+}
+
+static ALLEGRO_HAPTIC *  lhap_get_from_joystick(ALLEGRO_JOYSTICK * joy) {
+  return NULL;
+}
+
+static int lhap_get_num_axes(ALLEGRO_HAPTIC * haptic) {
+  return 1;
+}
+
+static bool lhap_is_effect_ok(ALLEGRO_HAPTIC * haptic, ALLEGRO_HAPTIC_EFFECT * effect) {
+   return false;
+}
+
+static bool lhap_upload_effect(ALLEGRO_HAPTIC * haptic, ALLEGRO_HAPTIC_EFFECT * effect, int * playid) {
+  return false;
+}
+
+static bool lhap_play_effect(ALLEGRO_HAPTIC * haptic, int repeats, int playid) {
+  return false;  
+}
+
+static bool lhap_stop_effect(ALLEGRO_HAPTIC * haptic, int playid) {
+  return false;
+}
+
+static bool lhap_is_effect_stopped(ALLEGRO_HAPTIC * haptic, int playid) {
+  return true;
+}
+
+static bool lhap_stop_all_effects(ALLEGRO_HAPTIC * haptic) {
+  return false;
+}
+
+static bool lhap_is_effect_playing(ALLEGRO_HAPTIC * haptic, int playid) {
+  return false;
+}
+
+
+
diff --git a/src/macosx/ohaptic.m b/src/macosx/ohaptic.m
new file mode 100644
index 0000000..e69de29
diff --git a/src/win/whaptic.c b/src/win/whaptic.c
new file mode 100644
index 0000000..e69de29
diff --git a/src/x/xsystem.c b/src/x/xsystem.c
index ae4c41e..81a6855 100644
--- a/src/x/xsystem.c
+++ b/src/x/xsystem.c
@@ -173,6 +173,13 @@ static ALLEGRO_JOYSTICK_DRIVER *xglx_get_joystick_driver(void)
    return _al_joystick_driver_list[0].driver;
 }
 
+
+static ALLEGRO_HAPTIC_DRIVER *xglx_get_haptic_driver(void)
+{
+   return _al_haptic_driver;
+}
+
+
 static int xglx_get_num_video_adapters(void)
 {
    ALLEGRO_SYSTEM_XGLX *system = (void *)al_get_system_driver();
@@ -239,6 +246,7 @@ ALLEGRO_SYSTEM_INTERFACE *_al_system_xglx_driver(void)
    xglx_vt->get_keyboard_driver = xglx_get_keyboard_driver;
    xglx_vt->get_mouse_driver = xglx_get_mouse_driver;
    xglx_vt->get_joystick_driver = xglx_get_joystick_driver;
+   xglx_vt->get_haptic_driver     = xglx_get_haptic_driver;
    xglx_vt->get_num_display_modes = xglx_get_num_display_modes;
    xglx_vt->get_display_mode = xglx_get_display_mode;
    xglx_vt->shutdown_system = xglx_shutdown_system;
@@ -251,6 +259,7 @@ ALLEGRO_SYSTEM_INTERFACE *_al_system_xglx_driver(void)
    xglx_vt->ungrab_mouse = _al_xwin_ungrab_mouse;
    xglx_vt->get_path = _al_unix_get_path;
    xglx_vt->inhibit_screensaver = xglx_inhibit_screensaver;
+   
 
    return xglx_vt;
 }
-- 
1.7.10.4


From 18648c8eb8c4b090262787858651b9501ef7362b Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Sat, 8 Jun 2013 23:17:28 +0200
Subject: [PATCH 2/7] Quick and very dirty commit before pushing to github

---
 include/allegro5/haptic.h |  102 ++++++++++++++++++++++++++++++++++++---------
 1 file changed, 83 insertions(+), 19 deletions(-)

diff --git a/include/allegro5/haptic.h b/include/allegro5/haptic.h
index 3da4415..a0ecd19 100644
--- a/include/allegro5/haptic.h
+++ b/include/allegro5/haptic.h
@@ -26,9 +26,9 @@
    extern "C" {
 #endif
 
-/* Enum: ALLEGRO_HAPTIC_FLAGS
+/* Enum: ALLEGRO_HAPTIC_CONSTANTS
  */
-enum ALLEGRO_HAPTIC_FLAGS { 
+enum ALLEGRO_HAPTIC_CONSTANTS { 
   ALLEGRO_HAPTIC_RUMBLE       = 1 << 0,
   ALLEGRO_HAPTIC_PERIODIC     = 1 << 1,
   ALLEGRO_HAPTIC_CONSTANT     = 1 << 2,
@@ -43,22 +43,29 @@ enum ALLEGRO_HAPTIC_FLAGS {
   ALLEGRO_HAPTIC_SAW_UP       = 1 << 11,
   ALLEGRO_HAPTIC_SAW_DOWN     = 1 << 12,
   ALLEGRO_HAPTIC_CUSTOM       = 1 << 13,  
-  ALLEGRO_HAPTIC_GAIN         = 1 << 14,  
-  ALLEGRO_HAPTIC_AUTOCENTER   = 1 << 15,  
+  ALLEGRO_HAPTIC_GAIN         = 1 << 14,   
+  ALLEGRO_HAPTIC_ANGLE        = 1 << 15,
+  ALLEGRO_HAPTIC_RADIUS       = 1 << 16,
+  ALLEGRO_HAPRIC_AZIMUTH      = 1 << 17,
 };
 
 
+
 /* Type: ALLEGRO_HAPTIC
  */
 typedef struct ALLEGRO_HAPTIC ALLEGRO_HAPTIC;
 
 /* Direction of a haptic effect. Angle is a value between 0 and 2*M_PI.
  * An angle 0 means oriented towards the user, M_PI is away from the user 
- * (towards the screen). 
- * Radius (if supported ) is the diistance of the effect from the user 
- * as a value between 0 and 1. Normally it is 0. 
+ * (towards the screen). Angle is only supported if the device capabilities include
+ * ALLEGRO_HAPTIC_ANGLE.  
+ * Radius (if supported ) is the distance of the effect from the user 
+ * as a value between 0 and 1. Normally it is 0. Radius is only supported if the 
+ * device capabilities include ALLEGRO_HAPTIC_RADIUS .  
  * Azimuth is the angle of elevation, between -M_PI and M_PI. 0 points to the 
  * horizontal plane, -M_PI points down, and M_PI points up.
+ * Azimuth is only supported if the device capabilities include 
+ * ALLEGRO_HAPTIC_AZIMUTH.
  * 
  */
 struct ALLEGRO_HAPTIC_DIRECTION {
@@ -67,11 +74,17 @@ struct ALLEGRO_HAPTIC_DIRECTION {
   double azimuth;
 };
 
+/* In all of the following structs, the doubles that express duration represent 
+ * time in seconds. The double that represent levels of intensity are between 0.0 
+ * and 1.0 that mean no effect and full 100% effect. */
+
+/* Delay to start the replay and duration of the replay, expressed  in seconds. */
 struct ALLEGRO_HAPTIC_REPLAY {
     double length;
     double delay;
 };
 
+/* Envelope of the effect. */
 struct ALLEGRO_HAPTIC_ENVELOPE {
     double attack_length;
     double attack_level;
@@ -79,17 +92,20 @@ struct ALLEGRO_HAPTIC_ENVELOPE {
     double fade_level;
 };
 
+/* Constant effect.  Level is between 0.0 and 1.0. */
 struct ALLEGRO_HAPTIC_CONSTANT_EFFECT {
     double level;
     struct ALLEGRO_HAPTIC_ENVELOPE envelope;
 };
 
+/* Ramp effect. Both start_level and end level are between 0.0 and 1.0.  */
 struct ALLEGRO_HAPTIC_RAMP_EFFECT {
     double start_level;
     double end_level;
     struct ALLEGRO_HAPTIC_ENVELOPE envelope;
 };
 
+/* Condition effect. */
 struct ALLEGRO_HAPTIC_CONDITION_EFFECT {
     double right_saturation;
     double left_saturation;
@@ -99,6 +115,7 @@ struct ALLEGRO_HAPTIC_CONDITION_EFFECT {
     double center;
 };
 
+/* Periodic (wave) effect. */
 struct ALLEGRO_HAPTIC_PERIODIC_EFFECT {
     int waveform;
     double period;
@@ -111,6 +128,8 @@ struct ALLEGRO_HAPTIC_PERIODIC_EFFECT {
     double *custom_data;
 };
 
+/* Simple rumble effect with a magnitude between 0.0 and 1.0 for both 
+ the strong and the weak rumble motors in the haptic device.  */
 struct ALLEGRO_HAPTIC_RUMBLE_EFFECT {
     double strong_magnitude;
     double weak_magnitude;
@@ -124,7 +143,8 @@ union ALLEGRO_HAPTIC_EFFECT_UNION {
     struct ALLEGRO_HAPTIC_RUMBLE_EFFECT     rumble;
 };
 
-/* Type: ALLEGRO_HAPTIC_EFFECT
+/* Type: ALLEGRO_HAPTIC_EFFECT. This neeeds to be filled in and uploaded to
+ * the haptic device before it can be played back. 
  */
 struct ALLEGRO_HAPTIC_EFFECT {
         int                                type;
@@ -141,35 +161,79 @@ typedef struct ALLEGRO_HAPTIC_EFFECT ALLEGRO_HAPTIC_EFFECT;
 
 
 
-
+/* Installs the haptic (force feedback) device subsystem. */
 AL_FUNC(bool,             al_install_haptic          , (void));
+/* Uninstalls the haptic device subsystem. */
 AL_FUNC(void,             al_uninstall_haptic        , (void));
+/* Returns true if the haptic device subsystem is installed, false if not. */
 AL_FUNC(bool,             al_is_haptic_installed     , (void));
+/* Checks if no new haptic devices became availabe and reconfigues the haptic 
+ *subsystem. */
 AL_FUNC(bool,             al_reconfigure_haptic      , (void));
 
+/* Gets the amount of available haptic devices.*/
 AL_FUNC(int,              al_get_num_haptics         , (void));
-AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic              , (int hapticn));
+
+/* Opens and initializes the haptic device and returns a pointer 
+ * to a device handle, or NULL on error.  */
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic             , (int hapticn));
+/* Closes the haptic device. */
 AL_FUNC(void,             al_release_haptic          , (ALLEGRO_HAPTIC *));
+/* Returns true if the haptic device can currently be used, false if not.*/
 AL_FUNC(bool,             al_get_haptic_active       , (ALLEGRO_HAPTIC *));
+/* Gets a string that describes the name of the haptic device.*/
 AL_FUNC(const char*,      al_get_haptic_name         , (ALLEGRO_HAPTIC *));
 
-AL_FUNC(int,              al_get_haptic_flags        , (ALLEGRO_HAPTIC *)); 
+/* Returns an integer with or'ed values from ALLEGRO_HAPTIC_CONSTANTS, that if
+ set indicate that the haptic device supports the given feature. */
+AL_FUNC(int,              al_get_haptic_capabilities , (ALLEGRO_HAPTIC *));
+
+/* Sets the gain of the haptic device if supported. Gain is much like volume for sound, 
+ it is as if every effect's intensity is multiplied by it. Gain is a value between 
+ 0.0 and 1.0. Returns true if set sucessfully, false if not.*/
+AL_FUNC(bool,             al_set_haptic_gain         , (ALLEGRO_HAPTIC *, double gain));
+/* Returns the current gain of the device. */
+AL_FUNC(double,           al_get_haptic_gain         , (ALLEGRO_HAPTIC *));
 
+/* Returns true if the mouse has haptic capabilities, false if not.*/
 AL_FUNC(bool,             al_is_mouse_haptic         , (ALLEGRO_MOUSE *));
+
+/* Returns true if the joystick has haptic capabilities, false if not.*/
 AL_FUNC(bool,             al_is_joystick_haptic      , (ALLEGRO_JOYSTICK *));
+/* If the mouse has haptic capabilities, returns the associated haptic device handle. 
+ * Otherwise returns NULL;*/
 AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_mouse   , (ALLEGRO_MOUSE *));
+/* If the mouse has haptic capabilities, returns the associated haptic device handle. 
+ * Otherwise returns NULL;*/
 AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_joystick, (ALLEGRO_JOYSTICK *));
-AL_FUNC(int,              al_get_haptic_num_axes     , (ALLEGRO_HAPTIC *)); 
+
+/* Returns the maximum amount of haptic effects that can be uploaded to the device. */
+AL_FUNC(int,              al_get_num_haptic_effects  , (ALLEGRO_HAPTIC *));
+
+/* Returns true if the haptic device can play the haptic effect as given, false if not. */
 AL_FUNC(bool,             al_is_haptic_effect_ok     , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
 
-AL_FUNC(bool,             al_upload_haptic_effect    , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, int * play_id));
-AL_FUNC(bool,             al_play_haptic_effect      , (ALLEGRO_HAPTIC *, int play_id, int loop));
-AL_FUNC(bool,             al_stop_haptic_effect      , (ALLEGRO_HAPTIC *, int play_id));
-AL_FUNC(bool,             al_is_haptic_effect_stopped, (ALLEGRO_HAPTIC *, int play_id));
-AL_FUNC(bool,             al_stop_all_haptic_effects , (ALLEGRO_HAPTIC *, int play_id));
-AL_FUNC(bool,             al_is_haptic_effect_playing, (ALLEGRO_HAPTIC *, int play_id));
-AL_FUNC(bool,             al_rumble_haptic           , (ALLEGRO_HAPTIC *, double intensity, double duration, int * play_id));
+/* Uploads the haptic effect to the device. In play_id, an integer is stored that is 
+ a reference to be used to control playback of the effect.*/
+AL_FUNC(bool,             al_upload_haptic_effect    , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
+
+/* Plays back a previously uploaded haptic effect on this device. */
+AL_FUNC(bool,             al_play_haptic_effect      , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT , * int loop));
+
+/* Stops playing a haptic effect on this device. */
+AL_FUNC(bool,             al_stop_haptic_effect      , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
+/* Stops playing all haptic effects on this device. */
+AL_FUNC(bool,             al_stop_all_haptic_effects , (ALLEGRO_HAPTIC *));
+
+/* Returns true if the haptic effect is playing on the device false if not or if stopped. */
+AL_FUNC(bool,             al_is_haptic_effect_playing, (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
+
+/* Uploads a simple rumble effect to the haptic device and starts playback immediately.
+ */
+AL_FUNC(bool,             al_rumble_haptic           , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, double intensity));
 
+/* Event source for haptic device events. 
+ *(XXX: currently no events are planned to be emitted, but that may change. )*/
 AL_FUNC(ALLEGRO_EVENT_SOURCE *, al_get_haptic_event_source, (void));
 
 
-- 
1.7.10.4


From 6f069bc8f25e23ad618aef1af91ecbf6ccb61e9b Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Fri, 21 Jun 2013 15:09:36 +0200
Subject: [PATCH 3/7] Interface and implementation of haptic devices with
 linux driver and an example.

---
 examples/ex_haptic.c                       |   58 ++-
 include/allegro5/haptic.h                  |  118 +++--
 include/allegro5/internal/aintern_haptic.h |   56 +--
 lib/Headers/allegro5/haptic.h              |  154 +++++--
 src/haptic.c                               |  238 ++++++----
 src/linux/lhaptic.c                        |  672 ++++++++++++++++++++--------
 6 files changed, 906 insertions(+), 390 deletions(-)

diff --git a/examples/ex_haptic.c b/examples/ex_haptic.c
index efaa0bf..c8dcf41 100644
--- a/examples/ex_haptic.c
+++ b/examples/ex_haptic.c
@@ -10,19 +10,31 @@
 
 #include "common.c"
 
+
 #define MAX_HAPTICS  32
 
 /* globals */
 ALLEGRO_EVENT_QUEUE  *event_queue;
 
-int num_haptics = 0;
-ALLEGRO_HAPTIC * haptics[MAX_HAPTICS];
 
 
 int main(void)
 {
    int index;
-   ALLEGRO_DISPLAY *display;
+   ALLEGRO_DISPLAY * display;
+   ALLEGRO_HAPTIC  * haptic = NULL;
+   ALLEGRO_HAPTIC_EFFECT effect = {0};
+   double intensity = 1.0;
+   double duration  = 1.0;
+   effect.type                           = ALLEGRO_HAPTIC_RUMBLE;
+   effect.data.rumble.strong_magnitude   = intensity;
+   effect.data.rumble.weak_magnitude     = intensity;  
+   effect.replay.delay                   = 0.1;
+   effect.replay.length                  = duration;
+   
+   
+   int num_joysticks;
+   double gain = -1.0;
 
    if (!al_init()) {
       abort_example("Could not init Allegro.\n");
@@ -32,7 +44,7 @@ int main(void)
    if (!display) {
       abort_example("al_create_display failed\n");
    }
-
+   al_install_joystick();
    al_install_haptic();
 
    event_queue = al_create_event_queue();
@@ -40,15 +52,37 @@ int main(void)
       abort_example("al_create_event_queue failed\n");
    }
    open_log();
-   num_haptics = al_get_num_haptics();
-   log_printf("Found %d haptic devices.\n", num_haptics);
-   for(index = 0; index < num_haptics; index++) {
-     ALLEGRO_HAPTIC * hap = al_get_haptic(index);; 
-     haptics[index] = hap;
-     if (hap) {  
-      log_printf("Opened device %d: %s.\n", al_get_haptic_name(hap));
+     
+   num_joysticks = al_get_num_joysticks();
+   for(index = 0; index < num_joysticks; index++) {
+     ALLEGRO_JOYSTICK * joy = al_get_joystick(index);
+     if(!joy) continue;
+     if (!al_is_joystick_haptic(joy)) {  
+       log_printf("Joystick %s does not support force feedback.\n", al_get_joystick_name(joy));
+       al_release_joystick(joy);
+       continue;
      } else {
-      log_printf("Could not open haptic device %d.\n", index);
+       ALLEGRO_HAPTIC_EFFECT_ID id;
+       log_printf("Joystick %s supports force feedback.\n", al_get_joystick_name(joy));
+       haptic = al_get_haptic_from_joystick(joy);
+       log_printf("Can play back %d haptic effects.\n", al_get_num_haptic_effects(haptic));
+       log_printf("Set gain: %d.\n", al_set_haptic_gain(haptic, 0.8));
+       log_printf("Get gain: %lf.\n", al_get_haptic_gain(haptic));
+       log_printf("Capabilities: %d.\n", al_get_haptic_capabilities(haptic));
+       log_printf("Upload: %d.\n: ", al_upload_haptic_effect(haptic, &effect, &id));
+       log_printf("Play: %d.\n: ", al_play_haptic_effect(&id, 5));
+       // al_rest(1.1 * 5 + 0.5);
+       while (al_is_haptic_effect_playing(&id)) {
+         //log_printf(".");
+       }
+       log_printf("Set gain: %d.\n", al_set_haptic_gain(haptic, 0.4));
+       log_printf("Play: %d.\n: ", al_play_haptic_effect(&id, 5));
+       while (al_is_haptic_effect_playing(&id)) {
+         //log_printf(".");
+       }
+       log_printf("Release: %d.\n: ", al_release_haptic_effect(&id));
+       
+       log_printf("\nAll done!\n");       
      }
    }
    
diff --git a/include/allegro5/haptic.h b/include/allegro5/haptic.h
index a0ecd19..2e5e1a3 100644
--- a/include/allegro5/haptic.h
+++ b/include/allegro5/haptic.h
@@ -161,80 +161,108 @@ typedef struct ALLEGRO_HAPTIC_EFFECT ALLEGRO_HAPTIC_EFFECT;
 
 
 
+/* Type: ALLEGRO_HAPTIC_EFFECT_ID
+ */
+typedef struct ALLEGRO_HAPTIC_EFFECT_ID ALLEGRO_HAPTIC_EFFECT_ID;
+
+struct ALLEGRO_HAPTIC_EFFECT_ID {
+  ALLEGRO_HAPTIC        * _haptic;
+  ALLEGRO_HAPTIC_EFFECT * _effect;
+  int                     _id;
+  int                     _handle;
+  bool                    _playing;  
+  double                  _started;
+  int                     _loops; 
+};
+
+
 /* Installs the haptic (force feedback) device subsystem. */
 AL_FUNC(bool,             al_install_haptic          , (void));
 /* Uninstalls the haptic device subsystem. */
 AL_FUNC(void,             al_uninstall_haptic        , (void));
 /* Returns true if the haptic device subsystem is installed, false if not. */
 AL_FUNC(bool,             al_is_haptic_installed     , (void));
-/* Checks if no new haptic devices became availabe and reconfigues the haptic 
- *subsystem. */
-AL_FUNC(bool,             al_reconfigure_haptic      , (void));
-
-/* Gets the amount of available haptic devices.*/
-AL_FUNC(int,              al_get_num_haptics         , (void));
-
-/* Opens and initializes the haptic device and returns a pointer 
- * to a device handle, or NULL on error.  */
-AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic             , (int hapticn));
-/* Closes the haptic device. */
-AL_FUNC(void,             al_release_haptic          , (ALLEGRO_HAPTIC *));
+
+/* Returns true if the mouse has haptic capabilities, false if not.*/
+AL_FUNC(bool,             al_is_mouse_haptic         , (ALLEGRO_MOUSE *));
+/* Returns true if the joystick has haptic capabilities, false if not.*/
+AL_FUNC(bool,             al_is_joystick_haptic      , (ALLEGRO_JOYSTICK *));
+/* Returns true if the keyboard has haptic capabilities, false if not.*/
+AL_FUNC(bool,             al_is_keyboard_haptic      , (ALLEGRO_KEYBOARD *));
+/* Returns true if the display has haptic capabilities, false if not.*/
+AL_FUNC(bool,             al_is_display_haptic       , (ALLEGRO_DISPLAY *));
+/* Returns true if the touch input has haptic capabilities, false if not.*/
+AL_FUNC(bool,             al_is_touch_input_haptic   , (ALLEGRO_TOUCH_INPUT *));
+
+
+/* If the mouse has haptic capabilities, returns the associated haptic device handle. 
+ * Otherwise returns NULL. */
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_mouse    , (ALLEGRO_MOUSE *));
+/* If the mouse has haptic capabilities, returns the associated haptic device handle. 
+ * Otherwise returns NULL. */
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_joystick , (ALLEGRO_JOYSTICK *));
+/* If the keyboard has haptic capabilities, returns the associated haptic device handle. 
+ * Otherwise returns NULL. */
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_keyboard , (ALLEGRO_KEYBOARD *));
+/* If the display has haptic capabilities, returns the associated haptic device handle. 
+ * Otherwise returns NULL. */
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_display  , (ALLEGRO_DISPLAY *));
+/* If the touch input has haptic capabilities, returns the associated haptic 
+ * device handle. Otherwise returns NULL. */
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_touch_input, (ALLEGRO_TOUCH_INPUT *));
+
+
+
+
 /* Returns true if the haptic device can currently be used, false if not.*/
-AL_FUNC(bool,             al_get_haptic_active       , (ALLEGRO_HAPTIC *));
-/* Gets a string that describes the name of the haptic device.*/
-AL_FUNC(const char*,      al_get_haptic_name         , (ALLEGRO_HAPTIC *));
+AL_FUNC(bool,   al_get_haptic_active       , (ALLEGRO_HAPTIC *));
 
 /* Returns an integer with or'ed values from ALLEGRO_HAPTIC_CONSTANTS, that if
  set indicate that the haptic device supports the given feature. */
-AL_FUNC(int,              al_get_haptic_capabilities , (ALLEGRO_HAPTIC *));
+AL_FUNC(int,    al_get_haptic_capabilities , (ALLEGRO_HAPTIC *));
 
 /* Sets the gain of the haptic device if supported. Gain is much like volume for sound, 
  it is as if every effect's intensity is multiplied by it. Gain is a value between 
  0.0 and 1.0. Returns true if set sucessfully, false if not.*/
-AL_FUNC(bool,             al_set_haptic_gain         , (ALLEGRO_HAPTIC *, double gain));
+AL_FUNC(bool,   al_set_haptic_gain         , (ALLEGRO_HAPTIC *, double gain));
 /* Returns the current gain of the device. */
-AL_FUNC(double,           al_get_haptic_gain         , (ALLEGRO_HAPTIC *));
+AL_FUNC(double, al_get_haptic_gain         , (ALLEGRO_HAPTIC *));
 
-/* Returns true if the mouse has haptic capabilities, false if not.*/
-AL_FUNC(bool,             al_is_mouse_haptic         , (ALLEGRO_MOUSE *));
-
-/* Returns true if the joystick has haptic capabilities, false if not.*/
-AL_FUNC(bool,             al_is_joystick_haptic      , (ALLEGRO_JOYSTICK *));
-/* If the mouse has haptic capabilities, returns the associated haptic device handle. 
- * Otherwise returns NULL;*/
-AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_mouse   , (ALLEGRO_MOUSE *));
-/* If the mouse has haptic capabilities, returns the associated haptic device handle. 
- * Otherwise returns NULL;*/
-AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_joystick, (ALLEGRO_JOYSTICK *));
 
 /* Returns the maximum amount of haptic effects that can be uploaded to the device. */
-AL_FUNC(int,              al_get_num_haptic_effects  , (ALLEGRO_HAPTIC *));
+AL_FUNC(int,    al_get_num_haptic_effects  , (ALLEGRO_HAPTIC *));
 
 /* Returns true if the haptic device can play the haptic effect as given, false if not. */
-AL_FUNC(bool,             al_is_haptic_effect_ok     , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
+AL_FUNC(bool,   al_is_haptic_effect_ok     , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
+
+/* Uploads the haptic effect to the device. In play_id, a handle is stored that is 
+ a reference to be used to control playback of the effect. */
+AL_FUNC(bool,   al_upload_haptic_effect    , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, ALLEGRO_HAPTIC_EFFECT_ID * play_id));
 
-/* Uploads the haptic effect to the device. In play_id, an integer is stored that is 
- a reference to be used to control playback of the effect.*/
-AL_FUNC(bool,             al_upload_haptic_effect    , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
+/* Plays back a previously uploaded haptic effect. */
+AL_FUNC(bool,   al_play_haptic_effect      , (ALLEGRO_HAPTIC_EFFECT_ID *, int loop));
 
-/* Plays back a previously uploaded haptic effect on this device. */
-AL_FUNC(bool,             al_play_haptic_effect      , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT , * int loop));
+/* Uploads and immediately plays back the haptic effect to the device. */
+AL_FUNC(bool,   al_upload_and_play_haptic_effect , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, int loop, ALLEGRO_HAPTIC_EFFECT_ID * play_id));
 
-/* Stops playing a haptic effect on this device. */
-AL_FUNC(bool,             al_stop_haptic_effect      , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
+/* Stops playing a haptic effect . */
+AL_FUNC(bool,   al_stop_haptic_effect      , (ALLEGRO_HAPTIC_EFFECT_ID *));
 /* Stops playing all haptic effects on this device. */
-AL_FUNC(bool,             al_stop_all_haptic_effects , (ALLEGRO_HAPTIC *));
+AL_FUNC(bool,   al_stop_all_haptic_effects , (ALLEGRO_HAPTIC *));
+
+/* Returns true if the haptic effect is playing or false if not or if stopped. */
+AL_FUNC(bool,   al_is_haptic_effect_playing, (ALLEGRO_HAPTIC_EFFECT_ID *));
+
+/* Releases the haptic effect from the device it has been uploaded to, allowing for 
+ other effects to be uploaded. */
+AL_FUNC(bool,   al_release_haptic_effect, (ALLEGRO_HAPTIC_EFFECT_ID *));
+
 
-/* Returns true if the haptic effect is playing on the device false if not or if stopped. */
-AL_FUNC(bool,             al_is_haptic_effect_playing, (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
 
 /* Uploads a simple rumble effect to the haptic device and starts playback immediately.
  */
-AL_FUNC(bool,             al_rumble_haptic           , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, double intensity));
+AL_FUNC(bool,   al_rumble_haptic, (ALLEGRO_HAPTIC *, double intensity, double duration, ALLEGRO_HAPTIC_EFFECT_ID *));
 
-/* Event source for haptic device events. 
- *(XXX: currently no events are planned to be emitted, but that may change. )*/
-AL_FUNC(ALLEGRO_EVENT_SOURCE *, al_get_haptic_event_source, (void));
 
 
 
diff --git a/include/allegro5/internal/aintern_haptic.h b/include/allegro5/internal/aintern_haptic.h
index f384c54..548e09f 100644
--- a/include/allegro5/internal/aintern_haptic.h
+++ b/include/allegro5/internal/aintern_haptic.h
@@ -19,25 +19,33 @@ typedef struct ALLEGRO_HAPTIC_DRIVER
    const char * hapdrv_ascii_name;
    AL_METHOD(bool, init_haptic, (void));
    AL_METHOD(void, exit_haptic, (void));
-   AL_METHOD(bool, reconfigure_haptics, (void));
-   AL_METHOD(int, num_haptics, (void));
-   AL_METHOD(ALLEGRO_HAPTIC *, get_haptic, (int));
-   AL_METHOD(void, release_haptic    , (ALLEGRO_HAPTIC *));
-   AL_METHOD(int, get_haptic_flags   , (ALLEGRO_HAPTIC *));
-   AL_METHOD(const char *, get_name  , (ALLEGRO_HAPTIC *));
-   AL_METHOD(bool, get_active        , (ALLEGRO_HAPTIC *));
+   
    AL_METHOD(bool, is_mouse_haptic   , (ALLEGRO_MOUSE *));
    AL_METHOD(bool, is_joystick_haptic, (ALLEGRO_JOYSTICK *));
+   AL_METHOD(bool, is_keyboard_haptic, (ALLEGRO_KEYBOARD *));
+   AL_METHOD(bool, is_display_haptic , (ALLEGRO_DISPLAY *));
+   AL_METHOD(bool, is_touch_input_haptic , (ALLEGRO_TOUCH_INPUT *));
+   
    AL_METHOD(ALLEGRO_HAPTIC *, get_from_mouse   , (ALLEGRO_MOUSE *));
    AL_METHOD(ALLEGRO_HAPTIC *, get_from_joystick, (ALLEGRO_JOYSTICK *));
-   AL_METHOD(int , get_num_axes      , (ALLEGRO_HAPTIC *)); 
+   AL_METHOD(ALLEGRO_HAPTIC *, get_from_keyboard, (ALLEGRO_KEYBOARD *));
+   AL_METHOD(ALLEGRO_HAPTIC *, get_from_display , (ALLEGRO_DISPLAY *));
+   AL_METHOD(ALLEGRO_HAPTIC *, get_from_touch_input, (ALLEGRO_TOUCH_INPUT *));
+
+   AL_METHOD(bool  , get_active        , (ALLEGRO_HAPTIC *)); 
+   AL_METHOD(int   , get_capabilities  , (ALLEGRO_HAPTIC *));   
+   AL_METHOD(double, get_gain          , (ALLEGRO_HAPTIC *));
+   AL_METHOD(bool  , set_gain          , (ALLEGRO_HAPTIC *, double));
+   AL_METHOD(int   , get_num_effects   , (ALLEGRO_HAPTIC *));   
+   
    AL_METHOD(bool, is_effect_ok      , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
-   AL_METHOD(bool, upload_effect     , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, int *));
-   AL_METHOD(bool, play_effect       , (ALLEGRO_HAPTIC *, int, int ));
-   AL_METHOD(bool, stop_effect       , (ALLEGRO_HAPTIC *, int));
-   AL_METHOD(bool, is_effect_stopped , (ALLEGRO_HAPTIC *, int));
-   AL_METHOD(bool, stop_all_efects   , (ALLEGRO_HAPTIC *, int));
-   AL_METHOD(bool, is_effect_playing , (ALLEGRO_HAPTIC *, int));
+   AL_METHOD(bool, upload_effect     , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, ALLEGRO_HAPTIC_EFFECT_ID *));
+   AL_METHOD(bool, play_effect       , (ALLEGRO_HAPTIC_EFFECT_ID *, int));
+   AL_METHOD(bool, stop_effect       , (ALLEGRO_HAPTIC_EFFECT_ID *));   
+   AL_METHOD(bool, is_effect_playing , (ALLEGRO_HAPTIC_EFFECT_ID *));   
+   AL_METHOD(bool, stop_all_efects   , (ALLEGRO_HAPTIC *));
+   AL_METHOD(bool, release_effect    , (ALLEGRO_HAPTIC_EFFECT_ID *));
+   
 } ALLEGRO_HAPTIC_DRIVER;
 
 
@@ -56,24 +64,22 @@ extern ALLEGRO_HAPTIC_DRIVER * _al_haptic_driver;
 
 #define _AL_HAPTIC_INFO_NAME_MAX            256
    
-/* Can playback at most 32 haptic effects at the same time. */   
+/* Can upload at most 32 haptic effects at the same time. */   
 #define _AL_HAPTIC_EFFECT_PLAYBACK_MAX      32    
 
    
-/* information about an entire haptic */
-typedef struct _AL_HAPTIC_INFO
-{
-  int  id;
-  int  flags; 
-  int  num_axes;
-  char name[_AL_HAPTIC_INFO_NAME_MAX];
-} _AL_HAPTIC_INFO;
-
+#define _AL_HAPTIC_FROM_JOYSTICK        1
+#define _AL_HAPTIC_FROM_MOUSE           2
+#define _AL_HAPTIC_FROM_KEYBOARD        3
+#define _AL_HAPTIC_FROM_DISPLAY         4
+#define _AL_HAPTIC_FROM_TOUCH_INPUT     5
 
 
 struct ALLEGRO_HAPTIC
 {
-   _AL_HAPTIC_INFO info;
+  int    from;
+  void * device;
+  double gain;
 };
 
 void _al_generate_haptic_event(ALLEGRO_EVENT *event);
diff --git a/lib/Headers/allegro5/haptic.h b/lib/Headers/allegro5/haptic.h
index 3da4415..2e5e1a3 100644
--- a/lib/Headers/allegro5/haptic.h
+++ b/lib/Headers/allegro5/haptic.h
@@ -26,9 +26,9 @@
    extern "C" {
 #endif
 
-/* Enum: ALLEGRO_HAPTIC_FLAGS
+/* Enum: ALLEGRO_HAPTIC_CONSTANTS
  */
-enum ALLEGRO_HAPTIC_FLAGS { 
+enum ALLEGRO_HAPTIC_CONSTANTS { 
   ALLEGRO_HAPTIC_RUMBLE       = 1 << 0,
   ALLEGRO_HAPTIC_PERIODIC     = 1 << 1,
   ALLEGRO_HAPTIC_CONSTANT     = 1 << 2,
@@ -43,22 +43,29 @@ enum ALLEGRO_HAPTIC_FLAGS {
   ALLEGRO_HAPTIC_SAW_UP       = 1 << 11,
   ALLEGRO_HAPTIC_SAW_DOWN     = 1 << 12,
   ALLEGRO_HAPTIC_CUSTOM       = 1 << 13,  
-  ALLEGRO_HAPTIC_GAIN         = 1 << 14,  
-  ALLEGRO_HAPTIC_AUTOCENTER   = 1 << 15,  
+  ALLEGRO_HAPTIC_GAIN         = 1 << 14,   
+  ALLEGRO_HAPTIC_ANGLE        = 1 << 15,
+  ALLEGRO_HAPTIC_RADIUS       = 1 << 16,
+  ALLEGRO_HAPRIC_AZIMUTH      = 1 << 17,
 };
 
 
+
 /* Type: ALLEGRO_HAPTIC
  */
 typedef struct ALLEGRO_HAPTIC ALLEGRO_HAPTIC;
 
 /* Direction of a haptic effect. Angle is a value between 0 and 2*M_PI.
  * An angle 0 means oriented towards the user, M_PI is away from the user 
- * (towards the screen). 
- * Radius (if supported ) is the diistance of the effect from the user 
- * as a value between 0 and 1. Normally it is 0. 
+ * (towards the screen). Angle is only supported if the device capabilities include
+ * ALLEGRO_HAPTIC_ANGLE.  
+ * Radius (if supported ) is the distance of the effect from the user 
+ * as a value between 0 and 1. Normally it is 0. Radius is only supported if the 
+ * device capabilities include ALLEGRO_HAPTIC_RADIUS .  
  * Azimuth is the angle of elevation, between -M_PI and M_PI. 0 points to the 
  * horizontal plane, -M_PI points down, and M_PI points up.
+ * Azimuth is only supported if the device capabilities include 
+ * ALLEGRO_HAPTIC_AZIMUTH.
  * 
  */
 struct ALLEGRO_HAPTIC_DIRECTION {
@@ -67,11 +74,17 @@ struct ALLEGRO_HAPTIC_DIRECTION {
   double azimuth;
 };
 
+/* In all of the following structs, the doubles that express duration represent 
+ * time in seconds. The double that represent levels of intensity are between 0.0 
+ * and 1.0 that mean no effect and full 100% effect. */
+
+/* Delay to start the replay and duration of the replay, expressed  in seconds. */
 struct ALLEGRO_HAPTIC_REPLAY {
     double length;
     double delay;
 };
 
+/* Envelope of the effect. */
 struct ALLEGRO_HAPTIC_ENVELOPE {
     double attack_length;
     double attack_level;
@@ -79,17 +92,20 @@ struct ALLEGRO_HAPTIC_ENVELOPE {
     double fade_level;
 };
 
+/* Constant effect.  Level is between 0.0 and 1.0. */
 struct ALLEGRO_HAPTIC_CONSTANT_EFFECT {
     double level;
     struct ALLEGRO_HAPTIC_ENVELOPE envelope;
 };
 
+/* Ramp effect. Both start_level and end level are between 0.0 and 1.0.  */
 struct ALLEGRO_HAPTIC_RAMP_EFFECT {
     double start_level;
     double end_level;
     struct ALLEGRO_HAPTIC_ENVELOPE envelope;
 };
 
+/* Condition effect. */
 struct ALLEGRO_HAPTIC_CONDITION_EFFECT {
     double right_saturation;
     double left_saturation;
@@ -99,6 +115,7 @@ struct ALLEGRO_HAPTIC_CONDITION_EFFECT {
     double center;
 };
 
+/* Periodic (wave) effect. */
 struct ALLEGRO_HAPTIC_PERIODIC_EFFECT {
     int waveform;
     double period;
@@ -111,6 +128,8 @@ struct ALLEGRO_HAPTIC_PERIODIC_EFFECT {
     double *custom_data;
 };
 
+/* Simple rumble effect with a magnitude between 0.0 and 1.0 for both 
+ the strong and the weak rumble motors in the haptic device.  */
 struct ALLEGRO_HAPTIC_RUMBLE_EFFECT {
     double strong_magnitude;
     double weak_magnitude;
@@ -124,7 +143,8 @@ union ALLEGRO_HAPTIC_EFFECT_UNION {
     struct ALLEGRO_HAPTIC_RUMBLE_EFFECT     rumble;
 };
 
-/* Type: ALLEGRO_HAPTIC_EFFECT
+/* Type: ALLEGRO_HAPTIC_EFFECT. This neeeds to be filled in and uploaded to
+ * the haptic device before it can be played back. 
  */
 struct ALLEGRO_HAPTIC_EFFECT {
         int                                type;
@@ -141,36 +161,108 @@ typedef struct ALLEGRO_HAPTIC_EFFECT ALLEGRO_HAPTIC_EFFECT;
 
 
 
+/* Type: ALLEGRO_HAPTIC_EFFECT_ID
+ */
+typedef struct ALLEGRO_HAPTIC_EFFECT_ID ALLEGRO_HAPTIC_EFFECT_ID;
+
+struct ALLEGRO_HAPTIC_EFFECT_ID {
+  ALLEGRO_HAPTIC        * _haptic;
+  ALLEGRO_HAPTIC_EFFECT * _effect;
+  int                     _id;
+  int                     _handle;
+  bool                    _playing;  
+  double                  _started;
+  int                     _loops; 
+};
+
 
+/* Installs the haptic (force feedback) device subsystem. */
 AL_FUNC(bool,             al_install_haptic          , (void));
+/* Uninstalls the haptic device subsystem. */
 AL_FUNC(void,             al_uninstall_haptic        , (void));
+/* Returns true if the haptic device subsystem is installed, false if not. */
 AL_FUNC(bool,             al_is_haptic_installed     , (void));
-AL_FUNC(bool,             al_reconfigure_haptic      , (void));
-
-AL_FUNC(int,              al_get_num_haptics         , (void));
-AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic              , (int hapticn));
-AL_FUNC(void,             al_release_haptic          , (ALLEGRO_HAPTIC *));
-AL_FUNC(bool,             al_get_haptic_active       , (ALLEGRO_HAPTIC *));
-AL_FUNC(const char*,      al_get_haptic_name         , (ALLEGRO_HAPTIC *));
-
-AL_FUNC(int,              al_get_haptic_flags        , (ALLEGRO_HAPTIC *)); 
 
+/* Returns true if the mouse has haptic capabilities, false if not.*/
 AL_FUNC(bool,             al_is_mouse_haptic         , (ALLEGRO_MOUSE *));
+/* Returns true if the joystick has haptic capabilities, false if not.*/
 AL_FUNC(bool,             al_is_joystick_haptic      , (ALLEGRO_JOYSTICK *));
-AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_mouse   , (ALLEGRO_MOUSE *));
-AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_joystick, (ALLEGRO_JOYSTICK *));
-AL_FUNC(int,              al_get_haptic_num_axes     , (ALLEGRO_HAPTIC *)); 
-AL_FUNC(bool,             al_is_haptic_effect_ok     , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
-
-AL_FUNC(bool,             al_upload_haptic_effect    , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, int * play_id));
-AL_FUNC(bool,             al_play_haptic_effect      , (ALLEGRO_HAPTIC *, int play_id, int loop));
-AL_FUNC(bool,             al_stop_haptic_effect      , (ALLEGRO_HAPTIC *, int play_id));
-AL_FUNC(bool,             al_is_haptic_effect_stopped, (ALLEGRO_HAPTIC *, int play_id));
-AL_FUNC(bool,             al_stop_all_haptic_effects , (ALLEGRO_HAPTIC *, int play_id));
-AL_FUNC(bool,             al_is_haptic_effect_playing, (ALLEGRO_HAPTIC *, int play_id));
-AL_FUNC(bool,             al_rumble_haptic           , (ALLEGRO_HAPTIC *, double intensity, double duration, int * play_id));
-
-AL_FUNC(ALLEGRO_EVENT_SOURCE *, al_get_haptic_event_source, (void));
+/* Returns true if the keyboard has haptic capabilities, false if not.*/
+AL_FUNC(bool,             al_is_keyboard_haptic      , (ALLEGRO_KEYBOARD *));
+/* Returns true if the display has haptic capabilities, false if not.*/
+AL_FUNC(bool,             al_is_display_haptic       , (ALLEGRO_DISPLAY *));
+/* Returns true if the touch input has haptic capabilities, false if not.*/
+AL_FUNC(bool,             al_is_touch_input_haptic   , (ALLEGRO_TOUCH_INPUT *));
+
+
+/* If the mouse has haptic capabilities, returns the associated haptic device handle. 
+ * Otherwise returns NULL. */
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_mouse    , (ALLEGRO_MOUSE *));
+/* If the mouse has haptic capabilities, returns the associated haptic device handle. 
+ * Otherwise returns NULL. */
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_joystick , (ALLEGRO_JOYSTICK *));
+/* If the keyboard has haptic capabilities, returns the associated haptic device handle. 
+ * Otherwise returns NULL. */
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_keyboard , (ALLEGRO_KEYBOARD *));
+/* If the display has haptic capabilities, returns the associated haptic device handle. 
+ * Otherwise returns NULL. */
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_display  , (ALLEGRO_DISPLAY *));
+/* If the touch input has haptic capabilities, returns the associated haptic 
+ * device handle. Otherwise returns NULL. */
+AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_touch_input, (ALLEGRO_TOUCH_INPUT *));
+
+
+
+
+/* Returns true if the haptic device can currently be used, false if not.*/
+AL_FUNC(bool,   al_get_haptic_active       , (ALLEGRO_HAPTIC *));
+
+/* Returns an integer with or'ed values from ALLEGRO_HAPTIC_CONSTANTS, that if
+ set indicate that the haptic device supports the given feature. */
+AL_FUNC(int,    al_get_haptic_capabilities , (ALLEGRO_HAPTIC *));
+
+/* Sets the gain of the haptic device if supported. Gain is much like volume for sound, 
+ it is as if every effect's intensity is multiplied by it. Gain is a value between 
+ 0.0 and 1.0. Returns true if set sucessfully, false if not.*/
+AL_FUNC(bool,   al_set_haptic_gain         , (ALLEGRO_HAPTIC *, double gain));
+/* Returns the current gain of the device. */
+AL_FUNC(double, al_get_haptic_gain         , (ALLEGRO_HAPTIC *));
+
+
+/* Returns the maximum amount of haptic effects that can be uploaded to the device. */
+AL_FUNC(int,    al_get_num_haptic_effects  , (ALLEGRO_HAPTIC *));
+
+/* Returns true if the haptic device can play the haptic effect as given, false if not. */
+AL_FUNC(bool,   al_is_haptic_effect_ok     , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
+
+/* Uploads the haptic effect to the device. In play_id, a handle is stored that is 
+ a reference to be used to control playback of the effect. */
+AL_FUNC(bool,   al_upload_haptic_effect    , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, ALLEGRO_HAPTIC_EFFECT_ID * play_id));
+
+/* Plays back a previously uploaded haptic effect. */
+AL_FUNC(bool,   al_play_haptic_effect      , (ALLEGRO_HAPTIC_EFFECT_ID *, int loop));
+
+/* Uploads and immediately plays back the haptic effect to the device. */
+AL_FUNC(bool,   al_upload_and_play_haptic_effect , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, int loop, ALLEGRO_HAPTIC_EFFECT_ID * play_id));
+
+/* Stops playing a haptic effect . */
+AL_FUNC(bool,   al_stop_haptic_effect      , (ALLEGRO_HAPTIC_EFFECT_ID *));
+/* Stops playing all haptic effects on this device. */
+AL_FUNC(bool,   al_stop_all_haptic_effects , (ALLEGRO_HAPTIC *));
+
+/* Returns true if the haptic effect is playing or false if not or if stopped. */
+AL_FUNC(bool,   al_is_haptic_effect_playing, (ALLEGRO_HAPTIC_EFFECT_ID *));
+
+/* Releases the haptic effect from the device it has been uploaded to, allowing for 
+ other effects to be uploaded. */
+AL_FUNC(bool,   al_release_haptic_effect, (ALLEGRO_HAPTIC_EFFECT_ID *));
+
+
+
+/* Uploads a simple rumble effect to the haptic device and starts playback immediately.
+ */
+AL_FUNC(bool,   al_rumble_haptic, (ALLEGRO_HAPTIC *, double intensity, double duration, ALLEGRO_HAPTIC_EFFECT_ID *));
+
 
 
 
diff --git a/src/haptic.c b/src/haptic.c
index 7dcdc6c..cf38874 100644
--- a/src/haptic.c
+++ b/src/haptic.c
@@ -33,7 +33,6 @@
 
 /* the active haptic driver */
 static ALLEGRO_HAPTIC_DRIVER *haptic_driver = NULL;
-static ALLEGRO_EVENT_SOURCE   haptic_es;
 
 
 /* Function: al_install_haptic
@@ -55,13 +54,11 @@ bool al_install_haptic(void)
       /* Avoid race condition in case the haptic driver generates an
        * event right after ->init_haptic.
        */
-      _al_event_source_init(&haptic_es);
       if (hapdrv && hapdrv->init_haptic()) {
          haptic_driver = hapdrv;
          _al_add_exit_func(al_uninstall_haptic, "al_uninstall_haptic");
          return true;
       }
-      _al_event_source_free(&haptic_es);
    }
 
    return false;
@@ -76,7 +73,6 @@ void al_uninstall_haptic(void)
    if (haptic_driver) {
       /* perform driver clean up */
       haptic_driver->exit_haptic();
-      _al_event_source_free(&haptic_es);
       haptic_driver = NULL;
    }
 }
@@ -90,149 +86,221 @@ bool al_is_haptic_installed(void)
 }
 
 
-/* Function: al_reconfigure_haptics
+
+
+/* Function: al_is_joystick_haptic
  */
-bool al_reconfigure_haptics(void)
+bool al_is_joystick_haptic(ALLEGRO_JOYSTICK * dev) 
 {
-   if (!haptic_driver)
-      return false;
-   
-   return haptic_driver->reconfigure_haptics();
+  ASSERT(dev);
+
+  return haptic_driver->is_joystick_haptic(dev);
 }
 
+/* Function: al_is_mouse_haptic
+ */
+bool al_is_mouse_haptic(ALLEGRO_MOUSE * mouse) 
+{
+  ASSERT(mouse);
 
+  return haptic_driver->is_mouse_haptic(mouse);
+}
 
-/* Function: al_get_haptic_event_source
+/* Function: al_is_keyboard_haptic
  */
-ALLEGRO_EVENT_SOURCE *al_get_haptic_event_source(void)
+bool al_is_keyboard_haptic(ALLEGRO_KEYBOARD * dev) 
 {
-   if (!haptic_driver)
-      return NULL;
-   return &haptic_es;
+  ASSERT(dev);
+
+  return haptic_driver->is_keyboard_haptic(dev);
 }
 
+/* Function: al_is_display_haptic
+ */
+bool al_is_display_haptic(ALLEGRO_DISPLAY * dev) 
+{
+  ASSERT(dev);
+
+  return haptic_driver->is_display_haptic(dev);
+}
 
 
-void _al_generate_haptic_event(ALLEGRO_EVENT *event)
+/* Function: al_get_haptic_from_joystick
+ */
+ALLEGRO_HAPTIC * al_get_haptic_from_joystick (ALLEGRO_JOYSTICK * dev) 
 {
-   ASSERT(haptic_driver);
-
-   _al_event_source_lock(&haptic_es);
-   if (_al_event_source_needs_to_generate_event(&haptic_es)) {
-      _al_event_source_emit_event(&haptic_es, event);
-   }
-   _al_event_source_unlock(&haptic_es);
+  ASSERT(dev);
+  
+  return haptic_driver->get_from_joystick(dev);
 }
 
+/* Function: al_get_haptic_from_mouse
+ */
+ALLEGRO_HAPTIC * al_get_haptic_from_mouse (ALLEGRO_MOUSE * dev) 
+{
+  ASSERT(dev);
+  
+  return haptic_driver->get_from_mouse(dev);
+}
 
+/* Function: al_get_haptic_from_keyboard
+ */
+ALLEGRO_HAPTIC * al_get_haptic_from_keyboard(ALLEGRO_KEYBOARD * dev) 
+{
+  ASSERT(dev);
+  
+  return haptic_driver->get_from_keyboard(dev);
+}
 
-/* Function: al_get_num_haptics
+/* Function: al_get_haptic_from_display
  */
-int al_get_num_haptics(void)
+ALLEGRO_HAPTIC * al_get_haptic_from_display(ALLEGRO_DISPLAY * dev) 
 {
-   if (haptic_driver)
-      return haptic_driver->num_haptics();
+  ASSERT(dev);
+  
+  return haptic_driver->get_from_display(dev);
+}
 
-   return 0;
+/* Function: al_get_haptic_from_touch_input
+ */
+ALLEGRO_HAPTIC * al_get_haptic_from_touch_input(ALLEGRO_TOUCH_INPUT * dev) {
+  ASSERT(dev);
+  
+  return haptic_driver->get_from_touch_input(dev);
 }
 
+/* Function: al_get_haptic_active
+ */
+bool al_get_haptic_active(ALLEGRO_HAPTIC *hap)
+{
+   ASSERT(hap);
+   
+   return haptic_driver->get_active(hap);
+}
 
 
-/* Function: al_get_haptic
+/* Function: al_get_haptic_flags
  */
-ALLEGRO_HAPTIC * al_get_haptic(int num)
+int al_get_haptic_capabilities(ALLEGRO_HAPTIC *hap)
 {
-   ASSERT(haptic_driver);
-   ASSERT(num >= 0);
+   ASSERT(hap);
 
-   return haptic_driver->get_haptic(num);
+   return haptic_driver->get_capabilities(hap);
 }
 
 
 
-/* Function: al_release_haptic
+/* Function: al_get_haptic_gain
  */
-void al_release_haptic(ALLEGRO_HAPTIC *hap)
+double al_get_haptic_gain (ALLEGRO_HAPTIC * hap) 
 {
-   ASSERT(haptic_driver);
    ASSERT(hap);
-
-   haptic_driver->release_haptic(hap);
+   
+   return haptic_driver->get_gain(hap);
 }
 
+/* Function: al_set_haptic_gain
+ */
+bool al_set_haptic_gain (ALLEGRO_HAPTIC * hap, double gain) 
+{
+  ASSERT(hap);
+     
+  return haptic_driver->set_gain(hap, gain);
+}
 
-
-/* Function: al_get_haptic_active
+/* Function: al_get_num_haptic_effects
  */
-bool al_get_haptic_active(ALLEGRO_HAPTIC *hap)
+int al_get_num_haptic_effects (ALLEGRO_HAPTIC * hap) 
 {
    ASSERT(hap);
 
-   return hap->info.flags;
+   return haptic_driver->get_num_effects(hap);
+}
+
+/* Function: al_is_haptic_effect_ok
+ */
+bool al_is_haptic_effect_ok(ALLEGRO_HAPTIC * hap, ALLEGRO_HAPTIC_EFFECT * effect) 
+{
+  ASSERT(hap);
+     
+  return haptic_driver->is_effect_ok(hap, effect);
 }
 
 
-/* Function: al_get_haptic_num_axes
+/* Function: al_upload_haptic_effect 
  */
-int al_get_haptic_num_axes(ALLEGRO_HAPTIC *hap)
+bool al_upload_haptic_effect(ALLEGRO_HAPTIC * hap, ALLEGRO_HAPTIC_EFFECT * effect,  ALLEGRO_HAPTIC_EFFECT_ID * id) 
 {
-   ASSERT(hap);
+  ASSERT(hap);  
+  
+  return haptic_driver->upload_effect(hap, effect, id);
+}
+
 
-   return hap->info.num_axes;
+/* Function: al_play_haptic_effect
+ */
+bool al_play_haptic_effect (ALLEGRO_HAPTIC_EFFECT_ID * id, int loop) 
+{
+  return haptic_driver->play_effect(id, loop);
 }
 
 
-/* Function: al_get_haptic_flags
+/* Function: al_upload_and_play_haptic_effect
  */
-int al_get_haptic_flags(ALLEGRO_HAPTIC *hap)
+bool al_upload_and_play_haptic_effect 
+(ALLEGRO_HAPTIC * hap, ALLEGRO_HAPTIC_EFFECT * effect , int loop, ALLEGRO_HAPTIC_EFFECT_ID * id) 
 {
-   ASSERT(hap);
+  if (!al_upload_haptic_effect(hap, effect, id)) return false;
+  return al_play_haptic_effect(id, loop);
+}
+
 
-   return haptic_driver->get_haptic_flags(hap);
+/* Function: al_stop_haptic_effect
+ */
+bool al_stop_haptic_effect (ALLEGRO_HAPTIC_EFFECT_ID * id) 
+{
+    return haptic_driver->stop_effect(id);
 }
 
+/* Function: al_stop_all_haptic_effects
+ */
+bool al_stop_all_haptic_effects (ALLEGRO_HAPTIC * hap) 
+{
+  return haptic_driver->stop_all_efects(hap);
+}
 
 
-/* Function: al_get_haptic_name
+/* Function: al_is_haptic_effect_playing
  */
-const char *al_get_haptic_name(ALLEGRO_HAPTIC *hap)
+bool al_is_haptic_effect_playing (ALLEGRO_HAPTIC_EFFECT_ID * id) 
 {
-   ASSERT(hap);
-   return haptic_driver->get_name(hap);
-   return NULL;
+  return haptic_driver->is_effect_playing(id);
 }
 
+
+/* Function: al_rumble_haptic
+ */
+bool al_rumble_haptic (ALLEGRO_HAPTIC * hap,  double intensity, double duration,  ALLEGRO_HAPTIC_EFFECT_ID * id) {
+  ALLEGRO_HAPTIC_EFFECT effect;
+  effect.type                           = ALLEGRO_HAPTIC_RUMBLE;
+  effect.data.rumble.strong_magnitude   = intensity;
+  effect.data.rumble.weak_magnitude     = intensity;  
+  effect.replay.delay                   = 0.0;
+  effect.replay.length                  = duration;
+  return al_upload_and_play_haptic_effect(hap, &effect, 1, id);
+}
+
+
 /*
-AL_FUNC(bool,             al_install_haptic          , (void));
-AL_FUNC(void,             al_uninstall_haptic        , (void));
-AL_FUNC(bool,             al_is_haptic_installed     , (void));
-AL_FUNC(bool,             al_reconfigure_haptic      , (void));
-
-AL_FUNC(int,              al_get_num_haptics         , (void));
-AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic              , (int hapticn));
-AL_FUNC(void,             al_release_haptic          , (ALLEGRO_HAPTIC *));
-AL_FUNC(bool,             al_get_haptic_active       , (ALLEGRO_HAPTIC *));
-AL_FUNC(const char*,      al_get_haptic_name         , (ALLEGRO_HAPTIC *));
-
-AL_FUNC(int,              al_get_haptic_flags        , (ALLEGRO_HAPTIC *)); 
-
-AL_FUNC(bool,             al_is_mouse_haptic         , (ALLEGRO_MOUSE *));
-AL_FUNC(bool,             al_is_joystick_haptic      , (ALLEGRO_JOYSTICK *));
-AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_mouse   , (ALLEGRO_MOUSE *));
-AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_joystick, (ALLEGRO_JOYSTICK *));
-AL_FUNC(int,              al_get_haptic_num_axes     , (ALLEGRO_HAPTIC *)); 
-AL_FUNC(bool,             al_is_haptic_effect_ok     , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *));
-
-AL_FUNC(bool,             al_upload_haptic_effect    , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, int * play_id));
-AL_FUNC(bool,             al_play_haptic_effect      , (ALLEGRO_HAPTIC *, int play_id, int loop));
-AL_FUNC(bool,             al_stop_haptic_effect      , (ALLEGRO_HAPTIC *, int play_id));
-AL_FUNC(bool,             al_is_haptic_effect_playing, (ALLEGRO_HAPTIC *, int play_id));
-AL_FUNC(bool,             al_stop_all_haptic_effects , (ALLEGRO_HAPTIC *, int play_id));
-AL_FUNC(bool,             al_is_haptic_stopped       , (ALLEGRO_HAPTIC *, int play_id));
-AL_FUNC(bool,             al_rumble_haptic           , (ALLEGRO_HAPTIC *, double intensity, double duration, int * play_id));
-
-
-*/
+ * Function: al_release_haptic_effect 
+ */
+bool al_release_haptic_effect (ALLEGRO_HAPTIC_EFFECT_ID * id) 
+{
+  return haptic_driver->release_effect(id);
+}
+
+
+
 
 
 
diff --git a/src/linux/lhaptic.c b/src/linux/lhaptic.c
index 3d0d69e..a97b030 100644
--- a/src/linux/lhaptic.c
+++ b/src/linux/lhaptic.c
@@ -17,6 +17,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stddef.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
@@ -33,14 +34,17 @@
 #define ALLEGRO_NO_COMPATIBILITY
 
 #include "allegro5/allegro.h"
+#include "allegro5/joystick.h"
 #include "allegro5/haptic.h"
 #include "allegro5/path.h"
 #include "allegro5/platform/alplatf.h"
 #include "allegro5/internal/aintern.h"
 #include "allegro5/internal/aintern_events.h"
 #include "allegro5/internal/aintern_haptic.h"
+#include "allegro5/internal/aintern_ljoynu.h"
 #include "allegro5/platform/aintunix.h"
-#include </home/bjorn/src/allegro/lib/Headers/allegro5/haptic.h>
+
+
 
 
 #if defined(ALLEGRO_HAVE_SYS_INOTIFY_H) && defined(ALLEGRO_HAVE_SYS_TIMERFD_H)
@@ -57,11 +61,17 @@ ALLEGRO_DEBUG_CHANNEL("lhaptic");
  *for everyone" for now. :p */
 #define HAPTICS_BUF_MAX         1000
 
+/* Support at most 16 effects per device. */
+#define HAPTICS_EFFECTS_MAX     16
+
+/* Tests if a bit in a byte array is set. */
+#define test_bit(bit, array)  (array [bit / (sizeof(long) * 8)] & (1 << (bit % (8 * sizeof(long)) )))
+
 
 typedef struct ALLEGRO_HAPTIC_LINUX
 {
+   struct ALLEGRO_HAPTIC parent;
    int in_use;
-   ALLEGRO_HAPTIC parent;
    int config_state;
    bool marked;
    int fd;
@@ -69,6 +79,7 @@ typedef struct ALLEGRO_HAPTIC_LINUX
    int state;
    char name[HAPTICS_BUF_MAX];
    int flags;
+   int effects[HAPTICS_EFFECTS_MAX];
 } ALLEGRO_HAPTIC_LINUX;
 
 
@@ -76,33 +87,34 @@ typedef struct ALLEGRO_HAPTIC_LINUX
 static bool lhap_init_haptic(void);
 static void lhap_exit_haptic(void);
 
-static bool lhap_reconfigure_haptics(void);
-static int lhap_num_haptics(void);
-static ALLEGRO_HAPTIC *lhap_get_haptic(int num);
-static void lhap_release_haptic(ALLEGRO_HAPTIC *hap_);
-
-static int lhap_get_flags(ALLEGRO_HAPTIC *hap_);
-static const char *lhap_get_name(ALLEGRO_HAPTIC *hap_);
 
 
-/* forward declarations 
-static bool lhap_get_active(ALLEGRO_HAPTIC *hap_);
-static void lhap_generate_event(ALLEGRO_HAPTIC_LINUX *joy, int button, ALLEGRO_EVENT_TYPE event_type);
-*/
 
-static bool lhap_get_active(ALLEGRO_HAPTIC *);
-static bool lhap_is_mouse_haptic(ALLEGRO_MOUSE *);
+static bool lhap_is_mouse_haptic(ALLEGRO_MOUSE * dev);
 static bool lhap_is_joystick_haptic(ALLEGRO_JOYSTICK *);
-static ALLEGRO_HAPTIC *  lhap_get_from_mouse(ALLEGRO_MOUSE *);
-static ALLEGRO_HAPTIC *  lhap_get_from_joystick(ALLEGRO_JOYSTICK *);
-static int               lhap_get_num_axes(ALLEGRO_HAPTIC *); 
-static bool lhap_is_effect_ok(ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *);
-static bool lhap_upload_effect(ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, int *);
-static bool lhap_play_effect(ALLEGRO_HAPTIC *, int, int);
-static bool lhap_stop_effect(ALLEGRO_HAPTIC *, int);
-static bool lhap_is_effect_stopped(ALLEGRO_HAPTIC *, int);
-static bool lhap_stop_all_effects(ALLEGRO_HAPTIC *);
-static bool lhap_is_effect_playing(ALLEGRO_HAPTIC *, int);
+static bool lhap_is_keyboard_haptic(ALLEGRO_KEYBOARD * dev);
+static bool lhap_is_display_haptic(ALLEGRO_DISPLAY * dev);
+static bool lhap_is_touch_input_haptic(ALLEGRO_TOUCH_INPUT * dev);
+
+static ALLEGRO_HAPTIC * lhap_get_from_mouse(ALLEGRO_MOUSE *dev);
+static ALLEGRO_HAPTIC * lhap_get_from_joystick(ALLEGRO_JOYSTICK *dev);
+static ALLEGRO_HAPTIC * lhap_get_from_keyboard(ALLEGRO_KEYBOARD *dev);
+static ALLEGRO_HAPTIC * lhap_get_from_display(ALLEGRO_DISPLAY *dev);
+static ALLEGRO_HAPTIC * lhap_get_from_touch_input(ALLEGRO_TOUCH_INPUT *dev);
+
+static bool   lhap_get_active(ALLEGRO_HAPTIC * hap);
+static int    lhap_get_capabilities(ALLEGRO_HAPTIC * dev);   
+static double lhap_get_gain(ALLEGRO_HAPTIC * dev);
+static bool   lhap_set_gain(ALLEGRO_HAPTIC * dev, double);
+static int    lhap_get_num_effects(ALLEGRO_HAPTIC * dev);   
+
+static bool lhap_is_effect_ok(ALLEGRO_HAPTIC *dev, ALLEGRO_HAPTIC_EFFECT *eff);
+static bool lhap_upload_effect(ALLEGRO_HAPTIC *dev, ALLEGRO_HAPTIC_EFFECT *eff, ALLEGRO_HAPTIC_EFFECT_ID * id);
+static bool lhap_play_effect(ALLEGRO_HAPTIC_EFFECT_ID * id, int loop);
+static bool lhap_stop_effect(ALLEGRO_HAPTIC_EFFECT_ID * id);
+static bool lhap_stop_all_effects(ALLEGRO_HAPTIC *dev);
+static bool lhap_is_effect_playing(ALLEGRO_HAPTIC_EFFECT_ID * id);
+static bool lhap_release_effect(ALLEGRO_HAPTIC_EFFECT_ID * id);
 
 /* The haptics driver vtable. */
 ALLEGRO_HAPTIC_DRIVER hapdrv_linux = 
@@ -113,32 +125,36 @@ ALLEGRO_HAPTIC_DRIVER hapdrv_linux =
    "Linux haptic(s)",
    lhap_init_haptic,
    lhap_exit_haptic,
-   lhap_reconfigure_haptics,
-   lhap_num_haptics,
-   lhap_get_haptic,
-   lhap_release_haptic,
-   lhap_get_flags,
-   lhap_get_name,
-   lhap_get_active,
+   
    lhap_is_mouse_haptic,
    lhap_is_joystick_haptic,
+   lhap_is_keyboard_haptic,
+   lhap_is_display_haptic,
+   lhap_is_touch_input_haptic,
+      
    lhap_get_from_mouse,
    lhap_get_from_joystick,
-   lhap_get_num_axes,
+   lhap_get_from_keyboard,
+   lhap_get_from_display,
+   lhap_get_from_touch_input,
+   
+   lhap_get_active,
+   lhap_get_capabilities,
+   lhap_get_gain,
+   lhap_set_gain,
+   lhap_get_num_effects,
+   
    lhap_is_effect_ok,
    lhap_upload_effect,
    lhap_play_effect,
    lhap_stop_effect,
-   lhap_is_effect_stopped,
+   lhap_is_effect_playing,
    lhap_stop_all_effects,
-   lhap_is_effect_playing
+   lhap_release_effect
 };
 
-
 ALLEGRO_HAPTIC_DRIVER * _al_haptic_driver = &hapdrv_linux;
 
-
-
 ALLEGRO_HAPTIC_DRIVER *_al_linux_haptic_driver(void)
 {
    return &hapdrv_linux;
@@ -146,144 +162,49 @@ ALLEGRO_HAPTIC_DRIVER *_al_linux_haptic_driver(void)
 
 
 
-static unsigned                 num_haptics = 0;  
+// static unsigned                 num_haptics = 0;  
 /* number of haptics known to the user */
 static ALLEGRO_HAPTIC_LINUX     haptics[HAPTICS_MAX];
-static ALLEGRO_MUTEX          * haptic_mutex;
-#ifdef SUPPORT_HOTPLUG
-
-static int                      haptic_inotify_fd = -1;
-static int                      haptic_timer_fd = -1;
+static ALLEGRO_MUTEX          * haptic_mutex = NULL;
 
-#endif
-
-/* Approach: to find the available haptic devices, scan 
- * the /sys/class/input directory for any directory named eventxxx. 
- * Then read  /sys/class/input/event1/capabilities/ff to find out the 
- * capabilities of the device. If it's 0, then no it doesn't support force feedback.
- * The idea of this approach is that the /dev file doesn' t have to be opened
- * to inspect the existence of the haptic device. 
- */
 
-/*
- * Scans the /sys/class/input/ directory to find any haptic devices and 
- * already sets them up to be used if needed. Returns the amount of haptics found
- * or negative or error.
- */
-int lhap_scan_haptics(void) {
-  char buf[HAPTICS_BUF_MAX];
-  char line[HAPTICS_BUF_MAX];
-  glob_t found;
-  int res;
-  unsigned int index;
-  num_haptics = 0;
+static bool lhap_init_haptic(void) {
+  int index;
+  haptic_mutex = al_create_mutex();
+  if (!haptic_mutex) return false;
   for (index = 0; index < HAPTICS_MAX; index ++) {
-    haptics[index].in_use = 0;
-    haptics[index].fd     = -1;
-  }  
-  res         = glob("/sys/class/input/event*", GLOB_MARK, NULL, &found);
-  if (res == GLOB_NOMATCH) { 
-    globfree(&found);
-    return -ENOENT;
-  }
-  for (index = 0; index < found.gl_pathc; index ++) {
-    int scanres;
-    unsigned int capa = 0, extra = 0;
-    FILE * fin;
-    char * path = found.gl_pathv[index]; 
-    memset(buf, 0, HAPTICS_BUF_MAX);
-    snprintf(buf, HAPTICS_BUF_MAX, "%sdevice/capabilities/ff", path);
-    fin = fopen(buf, "r");
-    if(!fin) {  continue;  }
-    scanres = fscanf(fin, "%u%u", &capa, &extra);
-    fclose(fin);
-    
-    if (capa > 0) { 
-      char * devname = strchr(strchr(path + 1, '/') + 1, '/');
-      /* It's a haptic device. */
-      ALLEGRO_HAPTIC_LINUX * hap = haptics + num_haptics;
-      hap->parent.info.id        = num_haptics;
-      num_haptics++;
-      hap->flags     = capa;
-      snprintf(hap->device_name, HAPTICS_BUF_MAX, "/dev%s", devname);
-      hap->device_name[strlen(hap->device_name) -1] = '\0';
-      snprintf(buf, HAPTICS_BUF_MAX, "%sdevice/name", path);
-      fin = fopen(buf, "r");
-      if (!fin) {  continue;  }
-      if(fgets(line, HAPTICS_BUF_MAX, fin)) {
-        line[HAPTICS_BUF_MAX-1] = '\0';
-        line[strlen(line)]      = '\0';
-        strcpy(hap->name, line);      
-      }
-      fclose(fin);      
-      fprintf(stderr, "Haptic device found: %s (%s) (%s), %d %d %d\n", buf, hap->device_name, hap->name, scanres, capa, extra);
-      
-    }
-    
+    haptics[index].in_use = false;
   }
-  globfree(&found);  
-  return num_haptics;
-} 
-
-
-bool lhap_init_haptic(void) {
-  return lhap_scan_haptics() >= 0;
-}
-
-
-void lhap_exit_haptic() {
-   return;
-}
-
-static bool lhap_reconfigure_haptics(void){
-  return 0;
-}
-
-int lhap_num_haptics() {
-  return num_haptics;
+  
+  return true;
 }
 
-ALLEGRO_HAPTIC_LINUX * lhap_al2lin(ALLEGRO_HAPTIC * haptic) {
-  if(!haptic) return NULL;
-  ALLEGRO_HAPTIC_LINUX  * lhap = haptics + haptic->info.id;
-  /* Could also have used offsetof, but, hey... */
-  return lhap;
+static ALLEGRO_HAPTIC_LINUX * lhap_get_available_haptic() {
+  int index;
+  haptic_mutex = al_create_mutex();
+  if(!haptic_mutex) return false;
+  for (index = 0; index < HAPTICS_MAX; index ++) {
+    if(!haptics[index].in_use) {
+      haptics[index].in_use = true;
+      return haptics + index;
+    }
+  }
+  return NULL;
 }
 
-
-void lhap_release_haptic(ALLEGRO_HAPTIC * haptic) {
-  ALLEGRO_HAPTIC_LINUX  * lhap = lhap_al2lin(haptic);
-  ASSERT(haptic);
-  if (!lhap->in_use) return;
-  if (lhap->fd < 0) return;
-  close(lhap->fd);
+/* Converts a generic haptic device to a linux specific one. */
+static ALLEGRO_HAPTIC_LINUX * lhap_from_al(ALLEGRO_HAPTIC * hap) {
+  void * ptr = hap;
+  if (!ptr) return NULL;
+  return (ALLEGRO_HAPTIC_LINUX *) (ptr - offsetof(ALLEGRO_HAPTIC_LINUX, parent));
 }
 
 
-ALLEGRO_HAPTIC * lhap_get_haptic(int index) {
-  ALLEGRO_HAPTIC_LINUX * lhap;
-  if(index >= HAPTICS_MAX) return NULL;
-  lhap = haptics + index;
-  if (!lhap->in_use) {
-    lhap->fd     = open(lhap->device_name, O_RDWR);
-    if(lhap->fd < 0 ) return NULL;
-    lhap->in_use = 1;
-    return &lhap->parent;
-  } else {
-    return &lhap->parent;
-  }
-  return NULL;
-}
 
 
-const char * lhap_get_name(ALLEGRO_HAPTIC * haptic) {
-  ALLEGRO_HAPTIC_LINUX * lhap = lhap_al2lin(haptic);
-  return lhap->name;
-}
-
-int lhap_get_flags(ALLEGRO_HAPTIC * haptic) {
-  ALLEGRO_HAPTIC_LINUX * lhap = lhap_al2lin(haptic);
-  return lhap->flags;
+static void lhap_exit_haptic() {
+  al_destroy_mutex(haptic_mutex);
+  return;
 }
 
 
@@ -328,13 +249,26 @@ static bool lhap_time2lin(__u16 * res, double sec) {
   
   if (sec < 0.0)        return false; 
   if (sec > 32.767)     return false;
-  return (__u16) round(sec * 1000.0);
+  (*res) = (__u16) round(sec * 1000.0);
+  return true;
 }
 
+/* converts the time in seconds to a linux compatible time. 
+ * Return false if out of bounds. This one allows negative times. */
+static bool lhap_stime2lin(__s16 * res, double sec) {
+  ASSERT(res); 
+  
+  if (sec < -32.767)    return false; 
+  if (sec > 32.767)     return false;
+  (*res) = (__s16) round(sec * 1000.0);
+  return true;
+}
+
+
 /* Converts replay data to linux. */
-static bool lhap_replay2lin(struct ff_replay * lin, ALLEGRO_HAPTIC_REPLAY * al) {
-  if(!lhap_time2lin(&lin.delay, al.delay))  return false;
-  if(!lhap_time2lin(&lin.length, al.length)) return false;
+static bool lhap_replay2lin(struct ff_replay * lin, struct ALLEGRO_HAPTIC_REPLAY * al) {
+  if(!lhap_time2lin(&lin->delay, al->delay))  return false;
+  if(!lhap_time2lin(&lin->length, al->length)) return false;
   return true;
 }
 
@@ -344,96 +278,450 @@ static bool lhap_level2lin(__u16 * res, double level) {
   ASSERT(res);   
   if (level < 0.0)        return false; 
   if (level > 1.0)        return false;
-  return (__u16) round(level * ((double) 0x7fff));
+  (*res) = (__u16) round(level * ((double) 0x7fff));
+  return true;
+}
+
+
+/* Converts the level in range -1.0 to 1.0 to a linux compatible level. 
+ * Returns false if out of bounds. */
+static bool lhap_slevel2lin(__s16 * res, double level) {
+  ASSERT(res);   
+  if (level < -1.0)        return false; 
+  if (level > 1.0)        return false;
+  (*res) = (__s16) round(level * ((double) 0x7ffe));
+  return true;
+}
+
+
+/* Converts an allegro haptic effect envelope to the linux structure. */
+static bool lhap_envelope2lin(struct ff_envelope * lin, struct ALLEGRO_HAPTIC_ENVELOPE * al) {
+  if (!lhap_time2lin(&lin->attack_length, al->attack_length)) return false;
+  if (!lhap_time2lin(&lin->fade_length  , al->fade_length)) return false;
+  if (!lhap_level2lin(&lin->attack_level, al->attack_level)) return false;
+  if (!lhap_level2lin(&lin->fade_level, al->fade_level)) return false;
+  return true;
 }
 
 /* Converts a rumble effect to linux. */
-static bool lhap_rumble2lin(struct ff_rumble_effect * lin, ALLEGRO_HAPTIC_RUMBLE_EFFECT * al) {
+static bool lhap_rumble2lin(struct ff_rumble_effect * lin, struct ALLEGRO_HAPTIC_RUMBLE_EFFECT * al) {
   if(!lhap_level2lin(&lin->strong_magnitude, al->strong_magnitude)) return false;
   if(!lhap_level2lin(&lin->weak_magnitude  , al->weak_magnitude)) return false;
   return true;
 }
 
+
+/* Converts a constant effect to linux. */
+static bool lhap_constant2lin(struct ff_constant_effect * lin, struct ALLEGRO_HAPTIC_CONSTANT_EFFECT * al) {
+  if(!lhap_envelope2lin(&lin->envelope , &al->envelope)) return false;
+  if(!lhap_slevel2lin(&lin->level, al->level)) return false;  
+  return true;
+}
+
+/* Converts a ramp effect to linux. */
+static bool lhap_ramp2lin(struct ff_ramp_effect * lin, struct ALLEGRO_HAPTIC_RAMP_EFFECT * al) {
+  if(!lhap_envelope2lin(&lin->envelope , &al->envelope)) return false;
+  if(!lhap_slevel2lin(&lin->start_level, al->start_level)) return false;
+  if(!lhap_slevel2lin(&lin->end_level, al->end_level)) return false;
+  return true;
+}
+
+/* Converts a ramp effect to linux. */
+static bool lhap_condition2lin(struct ff_condition_effect * lin, struct ALLEGRO_HAPTIC_CONDITION_EFFECT * al) {
+  if(!lhap_slevel2lin(&lin->center      , al->center)) return false;
+  if(!lhap_level2lin(&lin->deadband    , al->deadband)) return false;
+  if(!lhap_slevel2lin(&lin->right_coeff , al->right_coeff)) return false;
+  if(!lhap_level2lin(&lin->right_saturation , al->right_saturation)) return false;  
+  if(!lhap_slevel2lin(&lin->left_coeff  , al->left_coeff)) return false;
+  if(!lhap_level2lin(&lin->left_saturation , al->left_saturation)) return false;
+  return true;
+}
+
+
 /* converts a periodic effect  to linux */
-static bool lhap_periodic2lin(struct ff_periodic_effect * lin, ALLEGRO_HAPTIC_PERIODIC_EFFECT * al) {
-  
+static bool lhap_periodic2lin(struct ff_periodic_effect * lin, struct ALLEGRO_HAPTIC_PERIODIC_EFFECT * al) {
+  if(!lhap_slevel2lin(&lin->magnitude, al->magnitude)) return false;
+  if(!lhap_stime2lin(&lin->offset, al->offset)) return false;
+  if(!lhap_time2lin(&lin->period, al->period)) return false;
+  if(!lhap_time2lin(&lin->phase, al->phase)) return false;
+  if(!lhap_wave2lin(&lin->waveform, al->waveform)) return false; 
+  if (al->custom_data) {
+    /* Custom data is not supported yet, because currently no Linux 
+     * haptic driver supports it. 
+     */
+    return false;
+  }
+  if(!lhap_envelope2lin(&lin->envelope , &al->envelope)) return false;
   return true;
 }
 
 /* Converts allegro haptic effect to linux haptic effect. */
 static bool lhap_effect2lin(struct ff_effect * lin, ALLEGRO_HAPTIC_EFFECT * al) {
-  if(!lhap_type2lin(&lin->type, al->type,) return false;
+  if(!lhap_type2lin(&lin->type, al->type)) return false;
   /* lin_effect->replay = effect->re; */
   lin->direction = (__u16) round(((double)0xC000 * al->direction.angle) / (2 * M_PI));
   lin->id        = -1;
-  if(!lhap_replay2lin(&lin->replay, &al->replay);
+  if(!lhap_replay2lin(&lin->replay, &al->replay)) return false;
   switch(lin->type) {
     case FF_RUMBLE:
-      if(!lhap_rumble2lin(&lin->rumble, &al->rumble);
+      if(!lhap_rumble2lin(&lin->u.rumble, &al->data.rumble)) return false;
       break;
     case FF_PERIODIC:      
-      if(!lhap_periodic2lin(&lin->periodic, al->data.periodic)) return false;
+      if(!lhap_periodic2lin(&lin->u.periodic, &al->data.periodic)) return false;
       break;
-      
+    case FF_CONSTANT: 
+      if(!lhap_constant2lin(&lin->u.constant, &al->data.constant)) return false;
+      break;
+    
+    case FF_RAMP:
+    if(!lhap_ramp2lin(&lin->u.ramp, &al->data.ramp)) return false;
+      break;
+    
+    case FF_SPRING:
+    case FF_FRICTION:
+    case FF_DAMPER:
+    case FF_INERTIA:      
+      if(!lhap_condition2lin(&lin->u.condition[0],  &al->data.condition)) return false;
+    break;
+    default:
+      return false;      
   }
      
-  return false;
+  return true;
 }
 
 
 
 static bool lhap_get_active(ALLEGRO_HAPTIC * haptic) {
-  ALLEGRO_HAPTIC_LINUX * lhap = lhap_al2lin(haptic);
+  ALLEGRO_HAPTIC_LINUX * lhap = lhap_from_al(haptic);
   return lhap->in_use;
 }
 
 static bool lhap_is_mouse_haptic(ALLEGRO_MOUSE * mouse) {
+  (void) mouse;
   return false;
 }
 
+#define LONG_BITS (sizeof(long) * 8)
+#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
+/* Tests if a bit in an array of longs is set. */
+#define TEST_BIT(nr, addr) \
+    (((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0)
+
+bool lhap_fd_can_ff(int fd) 
+{
+  long bitmask[NLONGS(EV_CNT)]   = {0};
+      
+  if (ioctl (fd, EVIOCGBIT(0, sizeof(bitmask)), bitmask) < 0) {
+    return false;
+  }
+  if (TEST_BIT(EV_FF, bitmask)) {
+     return true;
+  }
+  return false;
+}
+
+
+
+
 static bool lhap_is_joystick_haptic(ALLEGRO_JOYSTICK * joy) {
-   return false;
+  // int newfd = -1;
+  ALLEGRO_JOYSTICK_LINUX * ljoy = (ALLEGRO_JOYSTICK_LINUX *) joy;
+  if (!al_is_joystick_installed())      return false;
+  if (!al_get_joystick_active(joy))     return false;  
+  if (ljoy->fd <= 0)                    return false; 
+  // al_cstr(ljoy->device_name)
+  // newfd = open("/dev/input/event8", O_RDWR);  
+  // close(newfd);
+  return lhap_fd_can_ff(ljoy->fd); 
+}
+
+static bool lhap_is_display_haptic (ALLEGRO_DISPLAY * dev) {
+  (void) dev;
+  return false;
+}
+
+static bool lhap_is_keyboard_haptic (ALLEGRO_KEYBOARD * dev) {
+  (void) dev;
+  return false;
+}
+
+static bool lhap_is_touch_input_haptic (ALLEGRO_TOUCH_INPUT * dev) {
+  (void) dev;
+  return false;
 }
 
-static ALLEGRO_HAPTIC *  lhap_get_from_mouse(ALLEGRO_MOUSE * mouse) {
+
+static ALLEGRO_HAPTIC *  lhap_get_from_mouse(ALLEGRO_MOUSE * mouse) 
+{
+  (void) mouse;
   return NULL;
 }
 
-static ALLEGRO_HAPTIC *  lhap_get_from_joystick(ALLEGRO_JOYSTICK * joy) {
+
+#define TEST_CAPA(BIT, MASK, CAP, ALCAPA) do { \
+  if (TEST_BIT(FF_PERIODIC, bitmask)) { cap |= ALCAPA; } \
+} while (0)
+  
+
+static bool get_haptic_capabilities(int fd, int * capabilities) {
+
+  int cap = 0;
+  // unsigned long device_bits[(EV_MAX + 8) / sizeof(unsigned long)];  
+  unsigned long bitmask[NLONGS(FF_CNT)]   = {0};
+  if (ioctl (fd, EVIOCGBIT(EV_FF, sizeof(bitmask)), bitmask) < 0) {
+    perror ("EVIOCGBIT ioctl failed");
+    fprintf(stderr, "For fd %d\n", fd);
+    return false;
+  }
+  TEST_CAPA(FF_PERIODIC, bitmask, cap, ALLEGRO_HAPTIC_PERIODIC); 
+  TEST_CAPA(FF_RUMBLE, bitmask, cap, ALLEGRO_HAPTIC_RUMBLE); 
+  TEST_CAPA(FF_CONSTANT, bitmask, cap, ALLEGRO_HAPTIC_CONSTANT);
+  TEST_CAPA(FF_SPRING, bitmask, cap, ALLEGRO_HAPTIC_SPRING); 
+  TEST_CAPA(FF_FRICTION, bitmask, cap, ALLEGRO_HAPTIC_FRICTION);   
+  TEST_CAPA(FF_DAMPER, bitmask, cap, ALLEGRO_HAPTIC_DAMPER);   
+  TEST_CAPA(FF_INERTIA, bitmask, cap, ALLEGRO_HAPTIC_INERTIA); 
+  TEST_CAPA(FF_RAMP, bitmask, cap, ALLEGRO_HAPTIC_RAMP); 
+  TEST_CAPA(FF_SQUARE, bitmask, cap, ALLEGRO_HAPTIC_SQUARE); 
+  TEST_CAPA(FF_TRIANGLE, bitmask, cap, ALLEGRO_HAPTIC_TRIANGLE); 
+  TEST_CAPA(FF_SINE, bitmask, cap, ALLEGRO_HAPTIC_SINE); 
+  TEST_CAPA(FF_SAW_UP, bitmask, cap, ALLEGRO_HAPTIC_SAW_UP); 
+  TEST_CAPA(FF_SAW_DOWN, bitmask, cap, ALLEGRO_HAPTIC_SAW_DOWN); 
+  TEST_CAPA(FF_CUSTOM, bitmask, cap, ALLEGRO_HAPTIC_CUSTOM); 
+  TEST_CAPA(FF_GAIN, bitmask, cap, ALLEGRO_HAPTIC_GAIN); 
+  
+  (*capabilities) = cap;
+  
+  return true;
+}
+
+static ALLEGRO_HAPTIC *  lhap_get_from_joystick(ALLEGRO_JOYSTICK * joy) 
+{
+  int index;
+  ALLEGRO_HAPTIC_LINUX *lhap;
+  ALLEGRO_JOYSTICK_LINUX * ljoy = (ALLEGRO_JOYSTICK_LINUX *) joy;
+  
+  if (!al_is_joystick_haptic(joy)) return NULL;  
+  
+  al_lock_mutex(haptic_mutex);
+  
+  lhap = lhap_get_available_haptic();
+  if (!lhap) return NULL;
+  
+  lhap->parent.device = joy; 
+  lhap->parent.from   = _AL_HAPTIC_FROM_JOYSTICK;
+  
+  
+  lhap->fd            = ljoy->fd;
+  lhap->in_use        = true;
+  for (index = 0; index < HAPTICS_EFFECTS_MAX; index ++) {
+    lhap->effects[index] = -1; // negative means not in use. 
+  }
+  lhap->parent.gain   = 1.0;
+  get_haptic_capabilities(lhap->fd, &lhap->flags);
+  al_unlock_mutex(haptic_mutex);
+  return &(lhap->parent);
+}
+
+static ALLEGRO_HAPTIC * lhap_get_from_display (ALLEGRO_DISPLAY * dev) 
+{
+  (void) dev;
   return NULL;
 }
 
-static int lhap_get_num_axes(ALLEGRO_HAPTIC * haptic) {
-  return 1;
+static  ALLEGRO_HAPTIC * lhap_get_from_keyboard (ALLEGRO_KEYBOARD * dev) 
+{
+  (void) dev;
+  return NULL;
 }
 
-static bool lhap_is_effect_ok(ALLEGRO_HAPTIC * haptic, ALLEGRO_HAPTIC_EFFECT * effect) {
-   return false;
+static ALLEGRO_HAPTIC * lhap_get_from_touch_input (ALLEGRO_TOUCH_INPUT * dev) 
+{
+  (void) dev;
+  return NULL;
 }
 
-static bool lhap_upload_effect(ALLEGRO_HAPTIC * haptic, ALLEGRO_HAPTIC_EFFECT * effect, int * playid) {
-  return false;
+
+static int lhap_get_capabilities (ALLEGRO_HAPTIC * dev) 
+{
+  ALLEGRO_HAPTIC_LINUX * lhap = lhap_from_al(dev);
+  return lhap->flags;
 }
 
-static bool lhap_play_effect(ALLEGRO_HAPTIC * haptic, int repeats, int playid) {
-  return false;  
+
+static double lhap_get_gain(ALLEGRO_HAPTIC * dev) {
+  (void) dev;
+  ALLEGRO_HAPTIC_LINUX * lhap = lhap_from_al(dev);
+  /* Unfortunately there seems to be no API to GET gain, only to set?! 
+   * So, retururn the stored gain.
+   */
+  return lhap->parent.gain;
 }
 
-static bool lhap_stop_effect(ALLEGRO_HAPTIC * haptic, int playid) {
-  return false;
+static bool lhap_set_gain(ALLEGRO_HAPTIC * dev, double gain) {  
+  ALLEGRO_HAPTIC_LINUX * lhap = lhap_from_al(dev);
+  struct input_event ie ;  
+  lhap->parent.gain     = gain;
+  timerclear(&ie.time); 
+  ie.type     = EV_FF;
+  ie.code     = FF_GAIN;
+  ie.value    =  (__s32)((double)0xFFFF * gain);
+  if (write(lhap->fd, &ie, sizeof(ie)) < 0) {
+    return false;
+  }
+  return true;
 }
 
-static bool lhap_is_effect_stopped(ALLEGRO_HAPTIC * haptic, int playid) {
+int lhap_get_num_effects (ALLEGRO_HAPTIC * dev) {
+  ALLEGRO_HAPTIC_LINUX * lhap = lhap_from_al(dev);
+  int n_effects; /* Number of effects the device can play at the same time */
+  
+  if (ioctl(lhap->fd, EVIOCGEFFECTS, &n_effects) < 0) {
+    perror("Cannot check amount of effects");
+    fprintf(stderr, "on FD %d\n", lhap->fd);
+    return HAPTICS_EFFECTS_MAX;
+  } 
+  if (n_effects > HAPTICS_EFFECTS_MAX) return HAPTICS_EFFECTS_MAX;
+  return n_effects;
+}
+
+
+static bool lhap_is_effect_ok(ALLEGRO_HAPTIC * haptic, ALLEGRO_HAPTIC_EFFECT * effect) {
+  struct ff_effect leff;
+  int caps = al_get_haptic_capabilities(haptic);
+  if(!((caps & effect->type) == effect->type)) return false;  
+  if(!lhap_effect2lin(&leff, effect)) return false;
+  return true;  
+}
+
+static double lhap_effect_duration(ALLEGRO_HAPTIC_EFFECT * effect) {
+  return effect->replay.delay + effect->replay.length;   
+}
+
+
+static bool lhap_upload_effect(ALLEGRO_HAPTIC * dev, ALLEGRO_HAPTIC_EFFECT * effect, ALLEGRO_HAPTIC_EFFECT_ID * id) { 
+  ALLEGRO_HAPTIC_LINUX * lhap = lhap_from_al(dev);
+  struct ff_effect leff;
+  int index;
+  int found = -1;
+  
+  ASSERT(dev);
+  ASSERT(id);
+  ASSERT(effect);
+  
+  /* set id's values to indicate failure. */
+  id->_haptic   = NULL;
+  id->_effect   = NULL;
+  id->_id       = -1;
+  id->_handle   = -1;
+  
+  
+  if(!lhap_effect2lin(&leff, effect)) { 
+    return false;
+  }
+  
+  leff.id = -1;
+  
+  /* Find empty spot for effect . */
+  for(index = 0; index < al_get_num_haptic_effects(dev); index ++) {
+    if(lhap->effects[index] < 0) {
+      found = index; 
+      break;
+    }
+  }
+  
+  /* No more space for an effect. */
+  if(found < 0) {
+    return false;
+  }
+  
+  /* Upload effect. */
+  if (ioctl(lhap->fd, EVIOCSFF, &leff) < 0 ) {
+    return false;
+  }
+  
+  id->_haptic   = dev;
+  id->_effect   = effect;
+  id->_id       = found;
+  id->_handle   = leff.id;
+  id->_playing  = false;
+    
   return true;
 }
 
-static bool lhap_stop_all_effects(ALLEGRO_HAPTIC * haptic) {
+static bool lhap_play_effect(ALLEGRO_HAPTIC_EFFECT_ID * id, int loops) {
+  struct input_event     play;
+  ALLEGRO_HAPTIC_LINUX * lhap = (ALLEGRO_HAPTIC_LINUX *) id->_haptic;    
+  int fd                     ;
+  
+  if(!lhap) return false; 
+  
+  fd = lhap->fd;
+  
+  timerclear(&play.time);
+  play.type     = EV_FF;
+  play.code     = id->_handle; 
+  loops         = (loops < 0 ) ? 1 : loops; 
+  play.value    = loops; /* play: 1, stop: 0 */
+   
+  if (write(fd, (const void*) &play, sizeof(play)) < 0) {    
+    perror("Effect play failed.");
+    return false;
+  }
+  id->_playing = true;
+  id->_started = al_get_time(); 
+  id->_loops   = loops;
+  
+  return true;  
+}
+
+static bool lhap_stop_effect(ALLEGRO_HAPTIC_EFFECT_ID * id) {
+  struct input_event play;
+  ALLEGRO_HAPTIC_LINUX * lhap = (ALLEGRO_HAPTIC_LINUX *) id->_haptic;    
+  int loops                   = 0;
+  
+  if(!lhap) return false; 
+  
+   
+  play.type     = EV_FF;
+  play.code     = id->_handle; 
+  loops         = (loops < 0 ) ? 1 : loops;
+  
+  if (write(lhap->fd, (const void*) &play, sizeof(play)) < 0) {
+    return false;
+  }
+  id->_playing  = false;
+  return true;   
+}
+
+static bool lhap_stop_all_effects(ALLEGRO_HAPTIC * haptic) {  
+  ALLEGRO_HAPTIC_LINUX * lhap = (ALLEGRO_HAPTIC_LINUX *) haptic;    
+  
+  
   return false;
 }
 
-static bool lhap_is_effect_playing(ALLEGRO_HAPTIC * haptic, int playid) {
+static bool lhap_is_effect_playing(ALLEGRO_HAPTIC_EFFECT_ID * id) {  
+  double duration;
+  ASSERT(id); 
+  
+  if(!id->_playing) return false;
+  /* Since there is no Linux api to test this, use a timer to check if the 
+   effect has been playing longe enough to be finsihed or not. */
+  duration = lhap_effect_duration(id->_effect) * id->_loops;
+  if((id->_started + duration) >= al_get_time()) return true;  
   return false;
 }
 
+static bool lhap_release_effect(ALLEGRO_HAPTIC_EFFECT_ID * id) {
+  ALLEGRO_HAPTIC_LINUX * lhap =  (ALLEGRO_HAPTIC_LINUX *) id->_haptic;  
+  lhap_stop_effect(id);
+  
+  if (ioctl(lhap->fd, EVIOCRMFF, id->_handle) < 0) {
+    return false;
+  }  
+  lhap->effects[id->_id] = -1; // negative means not in use.
+  return true;
+}
 
 
-- 
1.7.10.4


From 6a62c78c16957a847ed87c3dfaee961ccb7131b0 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Mon, 24 Jun 2013 11:02:16 +0200
Subject: [PATCH 4/7] Documentation for haptics.

---
 docs/src/refman/haptic.txt |  386 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 386 insertions(+)
 create mode 100644 docs/src/refman/haptic.txt

diff --git a/docs/src/refman/haptic.txt b/docs/src/refman/haptic.txt
new file mode 100644
index 0000000..3913cb0
--- /dev/null
+++ b/docs/src/refman/haptic.txt
@@ -0,0 +1,386 @@
+# Haptic routines
+
+Haptic functions support force feedback and vibration on input devices.
+These functions are declared in the main Allegro header file:
+
+    #include <allegro5/allegro.h>
+
+## API: ALLEGRO_HAPTIC
+
+This is an abstract data type representing a haptic device that supports force 
+feedback or vibration.
+
+See also: [al_get_haptic_from_joystick]
+
+## API: ALLEGRO_HAPTIC_CONSTANTS
+
+This enum contains flags that are used to define haptic effects and capabilities. 
+If the flag is set in the return value of [al_get_haptic_capabilities], it means
+the device supports the given effect. The value of these flags should be set 
+into a [ALLEGRO_HAPTIC_EFFECT] struct to determine what kind of haptic effect 
+should be caused when it is played. 
+
+* ALLEGRO_HAPTIC_RUMBLE       - simple vibration effects
+* ALLEGRO_HAPTIC_PERIODIC     - periodic, wave-form effects
+* ALLEGRO_HAPTIC_CONSTANT     - constant effects
+* ALLEGRO_HAPTIC_SPRING       - spring effects
+* ALLEGRO_HAPTIC_FRICTION     - friction effects
+* ALLEGRO_HAPTIC_DAMPER       - damper effects
+* ALLEGRO_HAPTIC_INERTIA      - inertia effects
+* ALLEGRO_HAPTIC_RAMP         - ramp effects
+* ALLEGRO_HAPTIC_SQUARE       - square wave periodic effect
+* ALLEGRO_HAPTIC_TRIANGLE     - triangle wave periodic effect
+* ALLEGRO_HAPTIC_SINE         - sine wave periodic effect
+* ALLEGRO_HAPTIC_SAW_UP       - upwards saw wave periodic effect
+* ALLEGRO_HAPTIC_SAW_DOWN     - downwards saw wave periodic effect
+* ALLEGRO_HAPTIC_CUSTOM       - custom wave periodic effect
+* ALLEGRO_HAPTIC_GAIN         - the haptic device supports gain setting
+* ALLEGRO_HAPTIC_ANGLE        - the haptic device supports angle coordinates
+* ALLEGRO_HAPTIC_RADIUS       - the haptic device supports radius coordinates
+* ALLEGRO_HAPTIC_AZIMUTH      - the haptic device supports azimuth coordinates
+
+See also: [al_get_haptic_capabilities], [ALLEGRO_HAPTIC_EFFECT]
+
+## API: ALLEGRO_HAPTIC_EFFECT.
+
+This struct models a particular haptic or vibration effect. It needs to be filled 
+correctly in and uploaded to a haptic device, before the device can play it back. 
+
+*Fields:*
+* type - The type of the haptic effect. May be one of the [ALLEGRO_HAPTIC_CONSTANTS]
+   constants between or equal to [ALLEGRO_HAPTIC_RUMBLE] and [ALLEGRO_HAPTIC_RAMP].
+   
+   If `type` is set to [ALLEGRO_HAPTIC_RUMBLE], then the effect is a simple "rumble" 
+   or vibration effect that shakes the device. In some cases, such as on a mobile 
+   platform, the whole device may shake.
+   
+   If `type` is set to  [ALLEGRO_HAPTIC_PERIODIC], the effect is a shake or 
+   vibration of which the intensity is a periodic wave form. 
+   
+   If `type` is set to  [ALLEGRO_HAPTIC_CONSTANT], the effect is a constant 
+   pressure, motion  or push-back in a certain direction of the axes of the device. 
+   
+   If `type` is set to  [ALLEGRO_HAPTIC_SPRING], the effect is a springy kind of 
+   resistance against motion of the axes of the haptic device. 
+   
+   If `type` is set to  [ALLEGRO_HAPTIC_FRICTION], the effect is a friction kind
+   of resistance against motion of the axes of the haptic device. 
+   
+   If `type` is set to  [ALLEGRO_HAPTIC_DAMPER], the effect is a damper kind
+   of resistance against motion of the axes of the haptic device. 
+   
+   If `type` is set to  [ALLEGRO_HAPTIC_INERTIA], the effect causes inertia or 
+   slowness of motions on the axes of the haptic device. 
+     
+   If `type` is set to  [ALLEGRO_HAPTIC_RAMP], the effect causes a pressure 
+   or push-back that ramps up or down depending on the position of the axis.
+   
+* direction - The direction of location in 3D space where the effect should be 
+   played. Allegro haptic devices model directions in 3D space using spherical 
+   coordinates. However, the haptic device may not support localized effects, or may 
+   not support all coordinate components. 
+   
+   In Allegro's coordinate system, the value in `direction.angle` determines 
+   the planar angle between the effect and the direction of the user who holds the 
+   device, expressed in radians.  So, an effect with an angle 0.0 takes place in 
+   the direction of the user of the  haptic device, and an angle of PI means in the 
+   direction away  from the user. 
+   
+   If [al_get_haptic_capabilities] has the flag [ALLEGRO_HAPTIC_ANGLE] set, 
+   then setting `direction.angle`  is supported. Otherwise,  it is unsupported, 
+   and you should set it to 0.
+   
+   The value in `direction.radius` is a relative value between 0.0 and 1.0 that 
+   determines the relative distance from the center of the haptic device at which 
+   the effect will play back. A value of 0 means that the effect should play back 
+   at the center of the device. A value of 1.0 means that the effect should play 
+   back away from the center as far as is possible.  
+   
+   If [al_get_haptic_capabilities] 
+   has the flag [ALLEGRO_HAPTIC_RADIUS] set,  then setting `direction.radius`  
+   is supported. Otherwise, it is unsupported, and you should set it to 0.
+   
+   The value in `direction.azimuth` determines the elevation angle between the 
+   effect and the plane in which the user is holding the device, expressed in 
+   radians. An effect with an azimuth 0.0 plays back in the plane in which
+   the user is holding the device, an azimuth +PI/2 means the effect plays back 
+   vertically  above the user plane, and an azimuth -PI/2 means the effect plays 
+   back vertically below the user plane.
+   
+   If [al_get_haptic_capabilities] has the flag [ALLEGRO_HAPTIC_AZIMUTH] set, 
+   then setting `direction.azimuth` is supported. Otherwise,  it is unsupported, 
+   and you should set it to 0.   
+   
+* replay - Determines how the effect should be played back. 
+   `replay.length` is the duration in seconds of the effect, and `replay.delay`
+   is the time in seconds that the effect playback should be delayed when playback 
+   is started with [al_play_haptic_effect].
+   
+* data - Determines in detail the parameters of the haptic effect to play back.  
+
+   If `type` is set to [ALLEGRO_HAPTIC_RUMBLE], then `data.rumble.strong_magnitude` 
+   must be set to a relative magnitude between 0.0 and 1.0 to determine how intensely 
+   the "large" rumble motor of the haptic device will vibrate. And 
+   `data.rumble.weak_magnitude` must be set to relative magnitude between 0.0 
+   and 1.0  to determine how intensely the "weak" ruble motor of the haptic device 
+   will vibrate. Not all devices have a "weak" motor, in which case the value set 
+   in `data.rumble.weak_magnitude` will be ignored.
+  
+   If `type` is set to  [ALLEGRO_HAPTIC_PERIODIC], then `data.periodic.waveform` 
+   must be set to one of [ALLEGRO_HAPTIC_SQUARE], [ALLEGRO_HAPTIC_TRIANGLE],
+   [ALLEGRO_HAPTIC_SINE], [ALLEGRO_HAPTIC_SAW_UP], [ALLEGRO_HAPTIC_SAW_DOWN],
+   [ALLEGRO_HAPTIC_CUSTOM]. This will then determine the wave form of the vibration
+   effect that will be played on the haptic device. 
+  
+   In these cases, `data.periodic.period` muust be set to the period in seconds 
+   of the wave form. The field `data.periodic.magnitude must be set to the relative 
+   magnitude of intensity between -1.0 and 1.0 at  which the wave form of the 
+   vibration  will be played back. The field `data.periodic.offset` must be filled 
+   in with the  offset from origin in seconds of the wave form of vibration. And the 
+   field `data.periodic.pahe` is the phase of the wave form of vibration in seconds.
+  
+   If `data.periodic.waveform` is set to [ALLEGRO_HAPTIC_CUSTOM], then 
+   `data.periodic.custom_data` must point to an array of `data.periodic.custom_len`
+   doubles, each with values between -1.0 and 1.0. This value array 
+   will determine the shape of the wave form of the haptic effect. 
+   [ALLEGRO_HAPTIC_CUSTOM] is not supported on some platforms, so use 
+   [al_get_haptic_capabilities] to check if it's available. If not, then 
+   it's a good idea play back a non-cusytom wave effect in stead as a substitute. 
+  
+   If `type` is set to  [ALLEGRO_HAPTIC_CONSTANT], then `data.constant.level` 
+   must be set to a relative intensity value between 0.0 and 1.0 to determine the 
+   intensity of the effect.
+   
+   
+   If `type` is set to  any of [ALLEGRO_HAPTIC_SPRING], [ALLEGRO_HAPTIC_FRICTION], 
+   [ALLEGRO_HAPTIC_DAMPER], [ALLEGRO_HAPTIC_INERTIA], [ALLEGRO_HAPTIC_RAMP], then
+   the `data.condition` struct shuld be filled in. To explain this better, 
+   it's best to keep in mind that this  kind of effects is most useful for 
+   steering-wheel kind of devices, where resistance or inertia should be applied
+   when turning the wheel of the device a certain distance to the right or the left. 
+   
+   The field `data.condition.right_saturation` must be filled in with a relative 
+   magnitude between -1.0 and 1.0 to determine the the intensity of resistance 
+   or inertia on the "right" side of the axis. Likewise, 
+   `data.condition.left_saturation` must be filled in with a relative 
+   magnitude between -1.0 and 1.0 to determine the the intensity of resistance 
+   or inertia on the "left" side of the axis. 
+   
+   The field 'data.condition.deadband' must be filled in with a relative value 
+   between 0.0 and 1.0, to determine the relative width of the "dead band" of the 
+   haptic effect. As long as the axis of the haptic device remains in the 
+   "dead band" area, the effect will not be applied. A value of 0.0 means there is 
+   no dead band, and a value of 1.0 means it applied over the whole range of the 
+   axis in question.   
+   
+   The field 'data.condition.center' must be filled in with a relative value between 
+   -1.0 and 1.0, to determine the relative position of the "center" of the effect 
+   around which the dead band is centered.  It should be set to 0.0 in case 
+   the center should not be shifted.
+   
+   The field `data.condition.right_coef`  and `data.condition.right_left_coef` must 
+   be filled in with a relative  coefficient, that will detemine how quickly the 
+   effect ramps up on the right and  left side. If set to 1.0, then the effect will 
+   be immediately at full intensity when outside of the dead band. If set to 0.0 
+   the effect will not be felt at all.
+
+   If ` type` is set to [ALLEGRO_HAPTIC_RAMP], then `data.ramp.start_level` should 
+   be set to a relative magnitude value between  -1.0 and 1.0 to determine the 
+   initial intensity of the haptic effect. The field  `data.ramp.end_level` should 
+   be set to a relative magnitude value between -1.0 and 1.0 to determine the final  
+   intensity of the haptic effect at the end of playback.
+  
+   If 'type' is set to any of [ALLEGRO_HAPTIC_PERIODIC], [ALLEGRO_HAPTIC_CONSTANT], 
+   [ALLEGRO_HAPTIC_RAMP], then  `data.envelope` determines the "envelope" of 
+   the effect. That is, it determines the duration and intensity for the ramp-up 
+   attack or "fade in" and the ramp-down "fade out" of the effect. 
+  
+   In these cases the field `data.envelope.attack_level` must be set to a relative 
+   value between 0.0 and 1.0 that determines the intensity the effect should have 
+   when it starts playing after `replay.delay` seconds have passed since the playback 
+   started. The field  `data.envelope.attack_length` must be set to the time in seconds 
+   that the effect should  ramp up to the maximum intensity as set by the other 
+   parameters of the effect. If `data.envelope.attack_length` is 0, 
+   then the effect will play immediately at full intensity. 
+  
+   The field `data.envelope.fade_level` must be set to a relative value between 
+   0.0 and 1.0 that determines the intensity the effect should have 
+   when at the moment it stops playing after `replay.length` + 'replay.delay' seconds 
+   have passed since the playback of the effect started. The field 
+   `data.envelope.fade_length` must be set to the time in seconds that the effect 
+   should fade out before it finished playing. If `data.envelope.fade_length` is 0, 
+   then the effect will not fade out.
+  
+   If you don't want to use an envelope, then set all four fields of
+   `data.envelope` to 0.0. The effect will then play back at full intensity 
+   throughout it's playback.
+   
+
+## API: ALLEGRO_HAPTIC_EFFECT_ID
+
+This struct is used as a handle to control playback of a haptic effect.
+The struct should be considered opaque. It's implementation is visible merely to 
+allow allocation by the users of the Allegro library. 
+
+## API: al_install_haptic 
+
+Installs the haptic (force feedback) device subsystem. This must be called before 
+using any other haptic related functions. Returns true if the haptics subsystem
+could be initialized correctly, false if not.
+
+## API: al_uninstall_haptic
+
+Uninstalls the haptic device subsystem.
+
+## API: al_is_haptic_installed
+
+Returns true if the haptic device subsystem is installed, false if not.
+
+## API: al_is_mouse_haptic
+
+Returns true if the mouse has haptic capabilities, false if not.
+
+## API: al_is_keyboard_haptic
+
+Returns true if the keyboard has haptic capabilities, false if not.
+
+## API: al_is_display_haptic
+
+Returns true if the display has haptic capabilities, false if not. To be more 
+precise, this is mainly meant for force feedback that shakes a hand held device,
+such as a phone or a tablet.
+
+## API: al_is_joystick_haptic
+
+Returns true if the joystick has haptic capabilities, false if not.
+
+## API: al_is_touch_input
+
+Returns true if thetouch input device has haptic capabilities, false if not.
+
+
+## API: al_get_haptic_from_mouse
+If the mouse has haptic capabilities, returns the associated haptic device handle. 
+Otherwise returns NULL.
+
+## API: al_get_haptic_from_keyboard
+If the keyboard has haptic capabilities, returns the associated haptic device handle. 
+Otherwise returns NULL.
+
+## API: al_get_haptic_from_display
+If the display has haptic capabilities, returns the associated haptic device handle. 
+Otherwise returns NULL.
+
+## API: al_get_haptic_from_joystick
+If the joystick has haptic capabilities, returns the associated haptic device handle. 
+Otherwise returns NULL. It's neccesary to call this again every time the joystick 
+configuration changes, such as though hot plugging.
+
+## API: al_get_haptic_from_touch_input
+If the touch input device has haptic capabilities, returns the associated haptic 
+device handle. Otherwise returns NULL.
+
+## API: al_release_haptic
+
+Releases the haptic device and it's resources when it's not needed anymore. 
+Should also be used in case the joystick configuration changed, such as when a 
+joystick is hot plugged. 
+
+
+## API:  al_get_haptic_active 
+
+Returns true if the haptic device can currently be used, false if not.
+
+## API:  al_get_haptic_capabilities
+
+Returns an integer with or'ed values from [ALLEGRO_HAPTIC_CONSTANTS], 
+which, if set, indicate that the haptic device supports the given feature. 
+
+## API: al_set_haptic_gain 
+
+Sets the gain of the haptic device if supported. Gain is much like volume for sound, 
+it is as if every effect's intensity is multiplied by it. Gain is a value between 
+0.0 and 1.0. Returns true if set sucessfully, false if not. Only works if 
+[al_get_haptic_capabilities] returns a value that has [ALLEGRO_HAPTIC_GAIN] set. 
+If not, this function returns false, and all effects will be played without any gain 
+influence.
+
+## API: al_get_haptic_gain 
+Returns the current gain of the device. Gain is much like volume for sound, 
+it is as if every effect's intensity is multiplied by it. Gain is a value between 
+0.0 and 1.0. Only works correctly if [al_get_haptic_capabilities] returns a value 
+that has [ALLEGRO_HAPTIC_GAIN] set. If this is not set, this function will simply 
+return 1.0 and all effects will be played without any gain influence.
+
+## API: al_get_num_haptic_effects
+
+Returns the maximum amount of haptic effects that can be uploaded to the device.
+This depends on the platform and the device.
+
+## API: al_is_haptic_effect_ok
+
+Returns true if the haptic device can play the haptic effect as given, false if not.
+The haptic effect must have been filled in completely and correctly.
+
+## API: al_upload_haptic_effect 
+
+Uploads the haptic effect to the device. 
+The haptic effect must have been filled in completely and correctly.
+You must also pass in a pointer to a user allocated [ALLEGRO_HAPTIC_EFFECT_ID]. 
+It it is stored a reference to be used to control playback of the effect. 
+Returns true if the effect was sucesfully uploaded, false if not.
+
+The same haptic effect can be uploaded several times, as long as care is taken to 
+pass in a different [ALLEGRO_HAPTIC_EFFECT_ID].
+
+## API: al_play_haptic_effect 
+
+Plays back a previously uploaded haptic effect. The play_id must be a valid
+[ALLEGRO_HAPTIC_EFFECT_ID] obtained from [al_upload_haptic_effect], 
+[al_upload_and_play_haptic_effect] or [al_rumble_haptic]. 
+
+The haptic effect will be played back loop times in sequence. If loop is less than 
+or equal to 1, then the effect will be played once only. 
+
+This function returns immediately and doesn't wait for the playback to finish. It 
+returns true if the playback was started sucessfully or false ir not.
+
+## API: al_upload_and_play_haptic_effect
+
+Uploads and immediately plays back the haptic effect to the device. Returns 
+true if the upload and playback were sucessful, false if either failed.
+
+See also: [al_upload_haptic_effect], [al_play_haptic_effect]
+
+## API: al_stop_haptic_effect
+
+Stops playing a previously uploaded haptic effect. The play_id must be a valid
+[ALLEGRO_HAPTIC_EFFECT_ID] obtained from [al_upload_haptic_effect], 
+[al_upload_and_play_haptic_effect] or [al_rumble_haptic]. 
+
+## API: al_is_haptic_effect_playing
+
+Returns true if the haptic effect is currently playing. Returns false 
+if the effect has been stopped or if it finsihed playing, or if it has not been 
+played yet. The play_id must be a valid [ALLEGRO_HAPTIC_EFFECT_ID] obtained from 
+[al_upload_haptic_effect], [al_upload_and_play_haptic_effect] or [al_rumble_haptic]. 
+
+## API: al_release_haptic_effect
+
+Releases a previously uploaded haptic effect from the device it has been uploaded 
+to, allowing for other effects to be uploaded. The play_id must be a valid 
+[ALLEGRO_HAPTIC_EFFECT_ID] obtained from [al_upload_haptic_effect], 
+[al_upload_and_play_haptic_effect] or [al_rumble_haptic]. 
+
+## API: al_rumble_haptic
+
+Uploads a simple rumble effect to the haptic device and starts playback immediately.
+The parameter `intensity` is a relative magnitude between 0.0 and 1.0 that 
+determines the intensity of the rumble effect. The `duration` determines the 
+duration of the effect in seconds.  
+
+You must also pass in a pointer to a user allocated [ALLEGRO_HAPTIC_EFFECT_ID]. 
+It it is stored a reference to be used to control playback of the effect. 
+Returns true if the rumble effect was succesfully uploaded and started, false if 
+not.
+
-- 
1.7.10.4


From b752b77b82422b866d4065c212d93e277be73ef2 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Mon, 24 Jun 2013 11:31:46 +0200
Subject: [PATCH 5/7] Documentation for haptics.

---
 docs/Refman.cmake          |    3 +-
 docs/inc.a.html            |   85 ++++++++++++++++++++++++++++++++++++++++++++
 docs/src/refman/haptic.txt |    6 ++--
 3 files changed, 90 insertions(+), 4 deletions(-)
 create mode 100644 docs/inc.a.html

diff --git a/docs/Refman.cmake b/docs/Refman.cmake
index 634359b..869228a 100644
--- a/docs/Refman.cmake
+++ b/docs/Refman.cmake
@@ -13,7 +13,8 @@ set(PAGES
     fshook
     fullscreen_mode
     graphics
-    joystick
+    haptic
+    joystick    
     keyboard
     memory
     misc
diff --git a/docs/inc.a.html b/docs/inc.a.html
new file mode 100644
index 0000000..79c8fee
--- /dev/null
+++ b/docs/inc.a.html
@@ -0,0 +1,85 @@
+<div class="sidebar">
+
+<div>
+<ul>
+<li><a href="index.html"><strong>Contents</strong></a></li>
+<li><a href="config.html">Configuration files</a></li>
+<li><a href="display.html">Display</a></li>
+<li><a href="events.html">Events</a></li>
+<li><a href="file.html">File I/O</a></li>
+<li><a href="fshook.html">Filesystem</a></li>
+<li><a href="fixed.html">Fixed point math</a></li>
+<li><a href="fullscreen_mode.html">Fullscreen modes</a></li>
+<li><a href="graphics.html">Graphics</a></li>
+<li><a href="haptic.html">Haptic</a></li>
+<li><a href="joystick.html">Joystick</a></li>
+<li><a href="keyboard.html">Keyboard</a></li>
+<li><a href="memory.html">Memory</a></li>
+<li><a href="monitor.html">Monitor</a></li>
+<li><a href="mouse.html">Mouse</a></li>
+<li><a href="path.html">Path</a></li>
+<li><a href="shader.html">Shader</a></li>
+<li><a href="state.html">State</a></li>
+<li><a href="system.html">System</a></li>
+<li><a href="threads.html">Threads</a></li>
+<li><a href="time.html">Time</a></li>
+<li><a href="timer.html">Timer</a></li>
+<li><a href="touch.html">Touch input</a></li>
+<li><a href="transformations.html">Transformations</a></li>
+<li><a href="utf8.html">UTF-8</a></li>
+<li><a href="misc.html">Miscellaneous</a></li>
+<li><a href="platform.html">Platform-specific</a></li>
+<li><a href="direct3d.html">Direct3D</a></li>
+<li><a href="opengl.html">OpenGL</a></li>
+</ul>
+<!-- The preceding blank line forces pandoc to terminate the list -->
+</div>
+
+<div>
+<ul>
+<li><a href="index.html#addons"><strong>Addons</strong></a></li>
+<li><a href="audio.html">Audio addon</a></li>
+<li><a href="acodec.html">Audio codecs</a></li>
+<li><a href="color.html">Color addon</a></li>
+<li><a href="font.html">Font addons</a></li>
+<li><a href="image.html">Image I/O addon</a></li>
+<li><a href="main.html">Main addon</a></li>
+<li><a href="memfile.html">Memfile addon</a></li>
+<li><a href="native_dialog.html">Native dialogs addon</a></li>
+<li><a href="physfs.html">PhysicsFS addon</a></li>
+<li><a href="primitives.html">Primitives addon</a></li>
+<li><a href="video.html">Video streaming addon</a></li>
+</ul>
+<!-- The preceding blank line forces pandoc to terminate the list -->
+</div>
+
+<div>
+<ul>
+<li><a href="index_all.html"><strong>Index</strong></a></li>
+</ul>
+<!-- The preceding blank line forces pandoc to terminate the list -->
+</div>
+
+<div class="searchbox">
+<script type="text/javascript">
+function on_search(index, control) {
+    // Note to self: the less-than sign must NOT converted to an entity!
+    // SCRIPT elements are special.  The HTML validator gives bad advice.
+    for (i = 0; i < search_index.length; i++) {
+        if (search_index[i] == control.keywords[index]) {
+            break;
+        }
+    }
+    location.href = search_urls[i];
+}
+</script>
+Search<br/> <input type="text" name="q" id="q" size="15" autocomplete="off"/><br/>
+<script type="text/javascript"> new autosuggest("q", search_index, null, on_search); </script>
+</div>
+
+</div>
+
+<div class="content">
+
+
+
diff --git a/docs/src/refman/haptic.txt b/docs/src/refman/haptic.txt
index 3913cb0..f22b94f 100644
--- a/docs/src/refman/haptic.txt
+++ b/docs/src/refman/haptic.txt
@@ -41,7 +41,7 @@ should be caused when it is played.
 
 See also: [al_get_haptic_capabilities], [ALLEGRO_HAPTIC_EFFECT]
 
-## API: ALLEGRO_HAPTIC_EFFECT.
+## API: ALLEGRO_HAPTIC_EFFECT
 
 This struct models a particular haptic or vibration effect. It needs to be filled 
 correctly in and uploaded to a haptic device, before the device can play it back. 
@@ -254,9 +254,9 @@ such as a phone or a tablet.
 
 Returns true if the joystick has haptic capabilities, false if not.
 
-## API: al_is_touch_input
+## API: al_is_touch_input_haptic
 
-Returns true if thetouch input device has haptic capabilities, false if not.
+Returns true if the touch input device has haptic capabilities, false if not.
 
 
 ## API: al_get_haptic_from_mouse
-- 
1.7.10.4


From 626d255f94534540d72d3d9fa7ad7e3963092b3b Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Mon, 24 Jun 2013 12:11:59 +0200
Subject: [PATCH 6/7] Bugfixes and refinements for haptic devices. Still no
 other drivers than Linux.

---
 examples/ex_haptic.c                       |   38 ++++++++--------
 include/allegro5/allegro.h                 |    1 +
 include/allegro5/haptic.h                  |   67 +++++++++++++++-------------
 include/allegro5/internal/aintern_haptic.h |    2 +-
 lib/Headers/allegro5/haptic.h              |   67 +++++++++++++++-------------
 src/haptic.c                               |   19 +++-----
 src/linux/lhaptic.c                        |   39 ++++++++--------
 7 files changed, 122 insertions(+), 111 deletions(-)

diff --git a/examples/ex_haptic.c b/examples/ex_haptic.c
index c8dcf41..d7e1cbf 100644
--- a/examples/ex_haptic.c
+++ b/examples/ex_haptic.c
@@ -1,11 +1,10 @@
 /*
- *    Example program for the Allegro library, by Peter Wang.
+ *    Example program for the Allegro library, by Beoran.
  *
- *    This program tests joystick events.
+ *    This program tests haptic effects.examples/ex_hapti
  */
 
 #include <allegro5/allegro.h>
-#include <allegro5/haptic.h>
 #include <allegro5/allegro_primitives.h>
 
 #include "common.c"
@@ -26,16 +25,14 @@ int main(void)
    ALLEGRO_HAPTIC_EFFECT effect = {0};
    double intensity = 1.0;
    double duration  = 1.0;
+   int num_joysticks;
+
    effect.type                           = ALLEGRO_HAPTIC_RUMBLE;
    effect.data.rumble.strong_magnitude   = intensity;
    effect.data.rumble.weak_magnitude     = intensity;  
    effect.replay.delay                   = 0.1;
-   effect.replay.length                  = duration;
-   
+   effect.replay.length                  = duration;   
    
-   int num_joysticks;
-   double gain = -1.0;
-
    if (!al_init()) {
       abort_example("Could not init Allegro.\n");
    }
@@ -66,27 +63,28 @@ int main(void)
        log_printf("Joystick %s supports force feedback.\n", al_get_joystick_name(joy));
        haptic = al_get_haptic_from_joystick(joy);
        log_printf("Can play back %d haptic effects.\n", al_get_num_haptic_effects(haptic));
-       log_printf("Set gain: %d.\n", al_set_haptic_gain(haptic, 0.8));
+       log_printf("Set gain to 0.8: %d.\n", al_set_haptic_gain(haptic, 0.8));
        log_printf("Get gain: %lf.\n", al_get_haptic_gain(haptic));
        log_printf("Capabilities: %d.\n", al_get_haptic_capabilities(haptic));
-       log_printf("Upload: %d.\n: ", al_upload_haptic_effect(haptic, &effect, &id));
-       log_printf("Play: %d.\n: ", al_play_haptic_effect(&id, 5));
-       // al_rest(1.1 * 5 + 0.5);
+       log_printf("Upload effect: %d.\n ", al_upload_haptic_effect(haptic, &effect, &id));
+       log_printf("Playing effect: %d.\n ", al_play_haptic_effect(&id, 5));
        while (al_is_haptic_effect_playing(&id)) {
-         //log_printf(".");
        }
-       log_printf("Set gain: %d.\n", al_set_haptic_gain(haptic, 0.4));
-       log_printf("Play: %d.\n: ", al_play_haptic_effect(&id, 5));
+       log_printf("Set gain to 0.4: %d.\n", al_set_haptic_gain(haptic, 0.4));
+       log_printf("Get gain: %lf.\n", al_get_haptic_gain(haptic));
+       log_printf("Playing effect again: %d.\n ", al_play_haptic_effect(&id, 5));
+       al_rest(2.0);
+       log_printf("Stopping effect: %d.\n ", al_stop_haptic_effect(&id));
+       
        while (al_is_haptic_effect_playing(&id)) {
          //log_printf(".");
        }
-       log_printf("Release: %d.\n: ", al_release_haptic_effect(&id));
-       
-       log_printf("\nAll done!\n");       
+       log_printf("Release effect: %d.\n ", al_release_haptic_effect(&id));
+       log_printf("Release haptic: %d.\n ", al_release_haptic(haptic));
      }
    }
-   
-   
+          
+   log_printf("\nAll done!\n");       
    close_log(true);
    // al_register_event_source(event_queue, al_get_display_event_source(display));
    // al_register_event_source(event_queue, al_get_haptic_event_source());
diff --git a/include/allegro5/allegro.h b/include/allegro5/allegro.h
index fe33fc8..e1d8f19 100644
--- a/include/allegro5/allegro.h
+++ b/include/allegro5/allegro.h
@@ -59,6 +59,7 @@
 #include "allegro5/touch_input.h"
 #include "allegro5/transformations.h"
 #include "allegro5/utf8.h"
+#include "allegro5/haptic.h"
 
 
 #ifndef ALLEGRO_NO_COMPATIBILITY
diff --git a/include/allegro5/haptic.h b/include/allegro5/haptic.h
index 2e5e1a3..7463bba 100644
--- a/include/allegro5/haptic.h
+++ b/include/allegro5/haptic.h
@@ -28,7 +28,8 @@
 
 /* Enum: ALLEGRO_HAPTIC_CONSTANTS
  */
-enum ALLEGRO_HAPTIC_CONSTANTS { 
+enum ALLEGRO_HAPTIC_CONSTANTS 
+{ 
   ALLEGRO_HAPTIC_RUMBLE       = 1 << 0,
   ALLEGRO_HAPTIC_PERIODIC     = 1 << 1,
   ALLEGRO_HAPTIC_CONSTANT     = 1 << 2,
@@ -46,7 +47,7 @@ enum ALLEGRO_HAPTIC_CONSTANTS {
   ALLEGRO_HAPTIC_GAIN         = 1 << 14,   
   ALLEGRO_HAPTIC_ANGLE        = 1 << 15,
   ALLEGRO_HAPTIC_RADIUS       = 1 << 16,
-  ALLEGRO_HAPRIC_AZIMUTH      = 1 << 17,
+  ALLEGRO_HAPTIC_AZIMUTH      = 1 << 17,
 };
 
 
@@ -62,13 +63,14 @@ typedef struct ALLEGRO_HAPTIC ALLEGRO_HAPTIC;
  * Radius (if supported ) is the distance of the effect from the user 
  * as a value between 0 and 1. Normally it is 0. Radius is only supported if the 
  * device capabilities include ALLEGRO_HAPTIC_RADIUS .  
- * Azimuth is the angle of elevation, between -M_PI and M_PI. 0 points to the 
- * horizontal plane, -M_PI points down, and M_PI points up.
+ * Azimuth is the angle of elevation, between -M_PI/2 and M_PI/2. 0 points to the 
+ * horizontal plane, -M_PI/2 points down, and M_PI/2 points up.
  * Azimuth is only supported if the device capabilities include 
  * ALLEGRO_HAPTIC_AZIMUTH.
  * 
  */
-struct ALLEGRO_HAPTIC_DIRECTION {
+struct ALLEGRO_HAPTIC_DIRECTION 
+{
   double angle; 
   double radius;
   double azimuth;
@@ -79,13 +81,15 @@ struct ALLEGRO_HAPTIC_DIRECTION {
  * and 1.0 that mean no effect and full 100% effect. */
 
 /* Delay to start the replay and duration of the replay, expressed  in seconds. */
-struct ALLEGRO_HAPTIC_REPLAY {
+struct ALLEGRO_HAPTIC_REPLAY 
+{
     double length;
     double delay;
 };
 
 /* Envelope of the effect. */
-struct ALLEGRO_HAPTIC_ENVELOPE {
+struct ALLEGRO_HAPTIC_ENVELOPE 
+{
     double attack_length;
     double attack_level;
     double fade_length;
@@ -93,20 +97,23 @@ struct ALLEGRO_HAPTIC_ENVELOPE {
 };
 
 /* Constant effect.  Level is between 0.0 and 1.0. */
-struct ALLEGRO_HAPTIC_CONSTANT_EFFECT {
+struct ALLEGRO_HAPTIC_CONSTANT_EFFECT 
+{
     double level;
     struct ALLEGRO_HAPTIC_ENVELOPE envelope;
 };
 
 /* Ramp effect. Both start_level and end level are between 0.0 and 1.0.  */
-struct ALLEGRO_HAPTIC_RAMP_EFFECT {
+struct ALLEGRO_HAPTIC_RAMP_EFFECT 
+{
     double start_level;
     double end_level;
     struct ALLEGRO_HAPTIC_ENVELOPE envelope;
 };
 
 /* Condition effect. */
-struct ALLEGRO_HAPTIC_CONDITION_EFFECT {
+struct ALLEGRO_HAPTIC_CONDITION_EFFECT 
+{
     double right_saturation;
     double left_saturation;
     double right_coeff;
@@ -116,7 +123,8 @@ struct ALLEGRO_HAPTIC_CONDITION_EFFECT {
 };
 
 /* Periodic (wave) effect. */
-struct ALLEGRO_HAPTIC_PERIODIC_EFFECT {
+struct ALLEGRO_HAPTIC_PERIODIC_EFFECT 
+{
     int waveform;
     double period;
     double magnitude;
@@ -130,12 +138,14 @@ struct ALLEGRO_HAPTIC_PERIODIC_EFFECT {
 
 /* Simple rumble effect with a magnitude between 0.0 and 1.0 for both 
  the strong and the weak rumble motors in the haptic device.  */
-struct ALLEGRO_HAPTIC_RUMBLE_EFFECT {
+struct ALLEGRO_HAPTIC_RUMBLE_EFFECT 
+{
     double strong_magnitude;
     double weak_magnitude;
 };
 
-union ALLEGRO_HAPTIC_EFFECT_UNION {
+union ALLEGRO_HAPTIC_EFFECT_UNION 
+{
     struct ALLEGRO_HAPTIC_CONSTANT_EFFECT   constant;
     struct ALLEGRO_HAPTIC_RAMP_EFFECT       ramp;
     struct ALLEGRO_HAPTIC_PERIODIC_EFFECT   periodic;
@@ -143,29 +153,26 @@ union ALLEGRO_HAPTIC_EFFECT_UNION {
     struct ALLEGRO_HAPTIC_RUMBLE_EFFECT     rumble;
 };
 
-/* Type: ALLEGRO_HAPTIC_EFFECT. This neeeds to be filled in and uploaded to
- * the haptic device before it can be played back. 
+/* Type: ALLEGRO_HAPTIC_EFFECT
  */
-struct ALLEGRO_HAPTIC_EFFECT {
-        int                                type;
-        int                                id;
-        struct ALLEGRO_HAPTIC_DIRECTION    direction;
-        struct ALLEGRO_HAPTIC_REPLAY       replay;
-        union ALLEGRO_HAPTIC_EFFECT_UNION  data; 
+struct ALLEGRO_HAPTIC_EFFECT 
+{
+  
+    int                                type;
+    struct ALLEGRO_HAPTIC_DIRECTION    direction;
+    struct ALLEGRO_HAPTIC_REPLAY       replay;
+    union ALLEGRO_HAPTIC_EFFECT_UNION  data; 
 };
 
-
-/* Type: ALLEGRO_HAPTIC_EFFECT
- */
 typedef struct ALLEGRO_HAPTIC_EFFECT ALLEGRO_HAPTIC_EFFECT;
 
 
-
 /* Type: ALLEGRO_HAPTIC_EFFECT_ID
  */
 typedef struct ALLEGRO_HAPTIC_EFFECT_ID ALLEGRO_HAPTIC_EFFECT_ID;
 
-struct ALLEGRO_HAPTIC_EFFECT_ID {
+struct ALLEGRO_HAPTIC_EFFECT_ID 
+{
   ALLEGRO_HAPTIC        * _haptic;
   ALLEGRO_HAPTIC_EFFECT * _effect;
   int                     _id;
@@ -211,6 +218,10 @@ AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_display  , (ALLEGRO_DISPLAY *));
  * device handle. Otherwise returns NULL. */
 AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_touch_input, (ALLEGRO_TOUCH_INPUT *));
 
+/* Releases the haptic device when it's not needed anymore. Should also be used in 
+ * case the joystick configuration changed, such as when a joystick is hot plugged.  
+ */
+AL_FUNC(bool, al_release_haptic, (ALLEGRO_HAPTIC *));
 
 
 
@@ -247,8 +258,6 @@ AL_FUNC(bool,   al_upload_and_play_haptic_effect , (ALLEGRO_HAPTIC *, ALLEGRO_HA
 
 /* Stops playing a haptic effect . */
 AL_FUNC(bool,   al_stop_haptic_effect      , (ALLEGRO_HAPTIC_EFFECT_ID *));
-/* Stops playing all haptic effects on this device. */
-AL_FUNC(bool,   al_stop_all_haptic_effects , (ALLEGRO_HAPTIC *));
 
 /* Returns true if the haptic effect is playing or false if not or if stopped. */
 AL_FUNC(bool,   al_is_haptic_effect_playing, (ALLEGRO_HAPTIC_EFFECT_ID *));
@@ -257,8 +266,6 @@ AL_FUNC(bool,   al_is_haptic_effect_playing, (ALLEGRO_HAPTIC_EFFECT_ID *));
  other effects to be uploaded. */
 AL_FUNC(bool,   al_release_haptic_effect, (ALLEGRO_HAPTIC_EFFECT_ID *));
 
-
-
 /* Uploads a simple rumble effect to the haptic device and starts playback immediately.
  */
 AL_FUNC(bool,   al_rumble_haptic, (ALLEGRO_HAPTIC *, double intensity, double duration, ALLEGRO_HAPTIC_EFFECT_ID *));
diff --git a/include/allegro5/internal/aintern_haptic.h b/include/allegro5/internal/aintern_haptic.h
index 548e09f..ebce39c 100644
--- a/include/allegro5/internal/aintern_haptic.h
+++ b/include/allegro5/internal/aintern_haptic.h
@@ -43,8 +43,8 @@ typedef struct ALLEGRO_HAPTIC_DRIVER
    AL_METHOD(bool, play_effect       , (ALLEGRO_HAPTIC_EFFECT_ID *, int));
    AL_METHOD(bool, stop_effect       , (ALLEGRO_HAPTIC_EFFECT_ID *));   
    AL_METHOD(bool, is_effect_playing , (ALLEGRO_HAPTIC_EFFECT_ID *));   
-   AL_METHOD(bool, stop_all_efects   , (ALLEGRO_HAPTIC *));
    AL_METHOD(bool, release_effect    , (ALLEGRO_HAPTIC_EFFECT_ID *));
+   AL_METHOD(bool, release           , (ALLEGRO_HAPTIC *));
    
 } ALLEGRO_HAPTIC_DRIVER;
 
diff --git a/lib/Headers/allegro5/haptic.h b/lib/Headers/allegro5/haptic.h
index 2e5e1a3..7463bba 100644
--- a/lib/Headers/allegro5/haptic.h
+++ b/lib/Headers/allegro5/haptic.h
@@ -28,7 +28,8 @@
 
 /* Enum: ALLEGRO_HAPTIC_CONSTANTS
  */
-enum ALLEGRO_HAPTIC_CONSTANTS { 
+enum ALLEGRO_HAPTIC_CONSTANTS 
+{ 
   ALLEGRO_HAPTIC_RUMBLE       = 1 << 0,
   ALLEGRO_HAPTIC_PERIODIC     = 1 << 1,
   ALLEGRO_HAPTIC_CONSTANT     = 1 << 2,
@@ -46,7 +47,7 @@ enum ALLEGRO_HAPTIC_CONSTANTS {
   ALLEGRO_HAPTIC_GAIN         = 1 << 14,   
   ALLEGRO_HAPTIC_ANGLE        = 1 << 15,
   ALLEGRO_HAPTIC_RADIUS       = 1 << 16,
-  ALLEGRO_HAPRIC_AZIMUTH      = 1 << 17,
+  ALLEGRO_HAPTIC_AZIMUTH      = 1 << 17,
 };
 
 
@@ -62,13 +63,14 @@ typedef struct ALLEGRO_HAPTIC ALLEGRO_HAPTIC;
  * Radius (if supported ) is the distance of the effect from the user 
  * as a value between 0 and 1. Normally it is 0. Radius is only supported if the 
  * device capabilities include ALLEGRO_HAPTIC_RADIUS .  
- * Azimuth is the angle of elevation, between -M_PI and M_PI. 0 points to the 
- * horizontal plane, -M_PI points down, and M_PI points up.
+ * Azimuth is the angle of elevation, between -M_PI/2 and M_PI/2. 0 points to the 
+ * horizontal plane, -M_PI/2 points down, and M_PI/2 points up.
  * Azimuth is only supported if the device capabilities include 
  * ALLEGRO_HAPTIC_AZIMUTH.
  * 
  */
-struct ALLEGRO_HAPTIC_DIRECTION {
+struct ALLEGRO_HAPTIC_DIRECTION 
+{
   double angle; 
   double radius;
   double azimuth;
@@ -79,13 +81,15 @@ struct ALLEGRO_HAPTIC_DIRECTION {
  * and 1.0 that mean no effect and full 100% effect. */
 
 /* Delay to start the replay and duration of the replay, expressed  in seconds. */
-struct ALLEGRO_HAPTIC_REPLAY {
+struct ALLEGRO_HAPTIC_REPLAY 
+{
     double length;
     double delay;
 };
 
 /* Envelope of the effect. */
-struct ALLEGRO_HAPTIC_ENVELOPE {
+struct ALLEGRO_HAPTIC_ENVELOPE 
+{
     double attack_length;
     double attack_level;
     double fade_length;
@@ -93,20 +97,23 @@ struct ALLEGRO_HAPTIC_ENVELOPE {
 };
 
 /* Constant effect.  Level is between 0.0 and 1.0. */
-struct ALLEGRO_HAPTIC_CONSTANT_EFFECT {
+struct ALLEGRO_HAPTIC_CONSTANT_EFFECT 
+{
     double level;
     struct ALLEGRO_HAPTIC_ENVELOPE envelope;
 };
 
 /* Ramp effect. Both start_level and end level are between 0.0 and 1.0.  */
-struct ALLEGRO_HAPTIC_RAMP_EFFECT {
+struct ALLEGRO_HAPTIC_RAMP_EFFECT 
+{
     double start_level;
     double end_level;
     struct ALLEGRO_HAPTIC_ENVELOPE envelope;
 };
 
 /* Condition effect. */
-struct ALLEGRO_HAPTIC_CONDITION_EFFECT {
+struct ALLEGRO_HAPTIC_CONDITION_EFFECT 
+{
     double right_saturation;
     double left_saturation;
     double right_coeff;
@@ -116,7 +123,8 @@ struct ALLEGRO_HAPTIC_CONDITION_EFFECT {
 };
 
 /* Periodic (wave) effect. */
-struct ALLEGRO_HAPTIC_PERIODIC_EFFECT {
+struct ALLEGRO_HAPTIC_PERIODIC_EFFECT 
+{
     int waveform;
     double period;
     double magnitude;
@@ -130,12 +138,14 @@ struct ALLEGRO_HAPTIC_PERIODIC_EFFECT {
 
 /* Simple rumble effect with a magnitude between 0.0 and 1.0 for both 
  the strong and the weak rumble motors in the haptic device.  */
-struct ALLEGRO_HAPTIC_RUMBLE_EFFECT {
+struct ALLEGRO_HAPTIC_RUMBLE_EFFECT 
+{
     double strong_magnitude;
     double weak_magnitude;
 };
 
-union ALLEGRO_HAPTIC_EFFECT_UNION {
+union ALLEGRO_HAPTIC_EFFECT_UNION 
+{
     struct ALLEGRO_HAPTIC_CONSTANT_EFFECT   constant;
     struct ALLEGRO_HAPTIC_RAMP_EFFECT       ramp;
     struct ALLEGRO_HAPTIC_PERIODIC_EFFECT   periodic;
@@ -143,29 +153,26 @@ union ALLEGRO_HAPTIC_EFFECT_UNION {
     struct ALLEGRO_HAPTIC_RUMBLE_EFFECT     rumble;
 };
 
-/* Type: ALLEGRO_HAPTIC_EFFECT. This neeeds to be filled in and uploaded to
- * the haptic device before it can be played back. 
+/* Type: ALLEGRO_HAPTIC_EFFECT
  */
-struct ALLEGRO_HAPTIC_EFFECT {
-        int                                type;
-        int                                id;
-        struct ALLEGRO_HAPTIC_DIRECTION    direction;
-        struct ALLEGRO_HAPTIC_REPLAY       replay;
-        union ALLEGRO_HAPTIC_EFFECT_UNION  data; 
+struct ALLEGRO_HAPTIC_EFFECT 
+{
+  
+    int                                type;
+    struct ALLEGRO_HAPTIC_DIRECTION    direction;
+    struct ALLEGRO_HAPTIC_REPLAY       replay;
+    union ALLEGRO_HAPTIC_EFFECT_UNION  data; 
 };
 
-
-/* Type: ALLEGRO_HAPTIC_EFFECT
- */
 typedef struct ALLEGRO_HAPTIC_EFFECT ALLEGRO_HAPTIC_EFFECT;
 
 
-
 /* Type: ALLEGRO_HAPTIC_EFFECT_ID
  */
 typedef struct ALLEGRO_HAPTIC_EFFECT_ID ALLEGRO_HAPTIC_EFFECT_ID;
 
-struct ALLEGRO_HAPTIC_EFFECT_ID {
+struct ALLEGRO_HAPTIC_EFFECT_ID 
+{
   ALLEGRO_HAPTIC        * _haptic;
   ALLEGRO_HAPTIC_EFFECT * _effect;
   int                     _id;
@@ -211,6 +218,10 @@ AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_display  , (ALLEGRO_DISPLAY *));
  * device handle. Otherwise returns NULL. */
 AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_touch_input, (ALLEGRO_TOUCH_INPUT *));
 
+/* Releases the haptic device when it's not needed anymore. Should also be used in 
+ * case the joystick configuration changed, such as when a joystick is hot plugged.  
+ */
+AL_FUNC(bool, al_release_haptic, (ALLEGRO_HAPTIC *));
 
 
 
@@ -247,8 +258,6 @@ AL_FUNC(bool,   al_upload_and_play_haptic_effect , (ALLEGRO_HAPTIC *, ALLEGRO_HA
 
 /* Stops playing a haptic effect . */
 AL_FUNC(bool,   al_stop_haptic_effect      , (ALLEGRO_HAPTIC_EFFECT_ID *));
-/* Stops playing all haptic effects on this device. */
-AL_FUNC(bool,   al_stop_all_haptic_effects , (ALLEGRO_HAPTIC *));
 
 /* Returns true if the haptic effect is playing or false if not or if stopped. */
 AL_FUNC(bool,   al_is_haptic_effect_playing, (ALLEGRO_HAPTIC_EFFECT_ID *));
@@ -257,8 +266,6 @@ AL_FUNC(bool,   al_is_haptic_effect_playing, (ALLEGRO_HAPTIC_EFFECT_ID *));
  other effects to be uploaded. */
 AL_FUNC(bool,   al_release_haptic_effect, (ALLEGRO_HAPTIC_EFFECT_ID *));
 
-
-
 /* Uploads a simple rumble effect to the haptic device and starts playback immediately.
  */
 AL_FUNC(bool,   al_rumble_haptic, (ALLEGRO_HAPTIC *, double intensity, double duration, ALLEGRO_HAPTIC_EFFECT_ID *));
diff --git a/src/haptic.c b/src/haptic.c
index cf38874..2dc0e06 100644
--- a/src/haptic.c
+++ b/src/haptic.c
@@ -86,8 +86,6 @@ bool al_is_haptic_installed(void)
 }
 
 
-
-
 /* Function: al_is_joystick_haptic
  */
 bool al_is_joystick_haptic(ALLEGRO_JOYSTICK * dev) 
@@ -262,14 +260,6 @@ bool al_stop_haptic_effect (ALLEGRO_HAPTIC_EFFECT_ID * id)
     return haptic_driver->stop_effect(id);
 }
 
-/* Function: al_stop_all_haptic_effects
- */
-bool al_stop_all_haptic_effects (ALLEGRO_HAPTIC * hap) 
-{
-  return haptic_driver->stop_all_efects(hap);
-}
-
-
 /* Function: al_is_haptic_effect_playing
  */
 bool al_is_haptic_effect_playing (ALLEGRO_HAPTIC_EFFECT_ID * id) 
@@ -291,8 +281,7 @@ bool al_rumble_haptic (ALLEGRO_HAPTIC * hap,  double intensity, double duration,
 }
 
 
-/*
- * Function: al_release_haptic_effect 
+/* Function: al_release_haptic_effect 
  */
 bool al_release_haptic_effect (ALLEGRO_HAPTIC_EFFECT_ID * id) 
 {
@@ -300,6 +289,12 @@ bool al_release_haptic_effect (ALLEGRO_HAPTIC_EFFECT_ID * id)
 }
 
 
+/* Function: al_release_haptic
+ */
+bool al_release_haptic(ALLEGRO_HAPTIC * haptic) 
+{
+  return haptic_driver->release(haptic);
+}
 
 
 
diff --git a/src/linux/lhaptic.c b/src/linux/lhaptic.c
index a97b030..0b3366f 100644
--- a/src/linux/lhaptic.c
+++ b/src/linux/lhaptic.c
@@ -102,6 +102,8 @@ static ALLEGRO_HAPTIC * lhap_get_from_keyboard(ALLEGRO_KEYBOARD *dev);
 static ALLEGRO_HAPTIC * lhap_get_from_display(ALLEGRO_DISPLAY *dev);
 static ALLEGRO_HAPTIC * lhap_get_from_touch_input(ALLEGRO_TOUCH_INPUT *dev);
 
+static bool lhap_release(ALLEGRO_HAPTIC * haptic);
+
 static bool   lhap_get_active(ALLEGRO_HAPTIC * hap);
 static int    lhap_get_capabilities(ALLEGRO_HAPTIC * dev);   
 static double lhap_get_gain(ALLEGRO_HAPTIC * dev);
@@ -112,7 +114,6 @@ static bool lhap_is_effect_ok(ALLEGRO_HAPTIC *dev, ALLEGRO_HAPTIC_EFFECT *eff);
 static bool lhap_upload_effect(ALLEGRO_HAPTIC *dev, ALLEGRO_HAPTIC_EFFECT *eff, ALLEGRO_HAPTIC_EFFECT_ID * id);
 static bool lhap_play_effect(ALLEGRO_HAPTIC_EFFECT_ID * id, int loop);
 static bool lhap_stop_effect(ALLEGRO_HAPTIC_EFFECT_ID * id);
-static bool lhap_stop_all_effects(ALLEGRO_HAPTIC *dev);
 static bool lhap_is_effect_playing(ALLEGRO_HAPTIC_EFFECT_ID * id);
 static bool lhap_release_effect(ALLEGRO_HAPTIC_EFFECT_ID * id);
 
@@ -149,8 +150,9 @@ ALLEGRO_HAPTIC_DRIVER hapdrv_linux =
    lhap_play_effect,
    lhap_stop_effect,
    lhap_is_effect_playing,
-   lhap_stop_all_effects,
-   lhap_release_effect
+   lhap_release_effect,
+   
+   lhap_release
 };
 
 ALLEGRO_HAPTIC_DRIVER * _al_haptic_driver = &hapdrv_linux;
@@ -199,9 +201,6 @@ static ALLEGRO_HAPTIC_LINUX * lhap_from_al(ALLEGRO_HAPTIC * hap) {
   return (ALLEGRO_HAPTIC_LINUX *) (ptr - offsetof(ALLEGRO_HAPTIC_LINUX, parent));
 }
 
-
-
-
 static void lhap_exit_haptic() {
   al_destroy_mutex(haptic_mutex);
   return;
@@ -675,17 +674,15 @@ static bool lhap_play_effect(ALLEGRO_HAPTIC_EFFECT_ID * id, int loops) {
   return true;  
 }
 
+
 static bool lhap_stop_effect(ALLEGRO_HAPTIC_EFFECT_ID * id) {
   struct input_event play;
   ALLEGRO_HAPTIC_LINUX * lhap = (ALLEGRO_HAPTIC_LINUX *) id->_haptic;    
-  int loops                   = 0;
-  
   if(!lhap) return false; 
   
-   
   play.type     = EV_FF;
   play.code     = id->_handle; 
-  loops         = (loops < 0 ) ? 1 : loops;
+  play.value    = 0;  
   
   if (write(lhap->fd, (const void*) &play, sizeof(play)) < 0) {
     return false;
@@ -694,20 +691,14 @@ static bool lhap_stop_effect(ALLEGRO_HAPTIC_EFFECT_ID * id) {
   return true;   
 }
 
-static bool lhap_stop_all_effects(ALLEGRO_HAPTIC * haptic) {  
-  ALLEGRO_HAPTIC_LINUX * lhap = (ALLEGRO_HAPTIC_LINUX *) haptic;    
-  
-  
-  return false;
-}
 
 static bool lhap_is_effect_playing(ALLEGRO_HAPTIC_EFFECT_ID * id) {  
   double duration;
   ASSERT(id); 
   
   if(!id->_playing) return false;
-  /* Since there is no Linux api to test this, use a timer to check if the 
-   effect has been playing longe enough to be finsihed or not. */
+  /* Since AFAICS there is no Linux API to test this, use a timer to check if the 
+   effect has been playing long enough to be finished or not. */
   duration = lhap_effect_duration(id->_effect) * id->_loops;
   if((id->_started + duration) >= al_get_time()) return true;  
   return false;
@@ -725,3 +716,15 @@ static bool lhap_release_effect(ALLEGRO_HAPTIC_EFFECT_ID * id) {
 }
 
 
+static bool lhap_release(ALLEGRO_HAPTIC * haptic) {
+  ALLEGRO_HAPTIC_LINUX * lhap = lhap_from_al(haptic);
+  
+  ASSERT(haptic);
+  
+  if(!lhap->in_use) return false;
+  
+  lhap->in_use = false;
+  lhap->fd     = -1;
+  return true;
+}
+
-- 
1.7.10.4


From 230946a2e460f59eea4aa6e946a1e35ed2c74029 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Mon, 24 Jun 2013 14:32:32 +0200
Subject: [PATCH 7/7] Small haptic documentation fix.

---
 docs/src/refman/haptic.txt |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/src/refman/haptic.txt b/docs/src/refman/haptic.txt
index f22b94f..026bc83 100644
--- a/docs/src/refman/haptic.txt
+++ b/docs/src/refman/haptic.txt
@@ -327,7 +327,7 @@ The haptic effect must have been filled in completely and correctly.
 Uploads the haptic effect to the device. 
 The haptic effect must have been filled in completely and correctly.
 You must also pass in a pointer to a user allocated [ALLEGRO_HAPTIC_EFFECT_ID]. 
-It it is stored a reference to be used to control playback of the effect. 
+This `id` can be used to control playback of the effect. 
 Returns true if the effect was sucesfully uploaded, false if not.
 
 The same haptic effect can be uploaded several times, as long as care is taken to 
-- 
1.7.10.4



Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/