[AD] Windows DirectInput haptic driver and some small haptic enhancements

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


I have implemented a Windows driver for haptic (force feedback)
devices that support the full DirectInput API. Notable exception to
this driver are the XBOX and compatible controllers for which haptics
only works though the XInput API. The driver compiles under Mingw but
wasn't tested under MSVC yet. Also, it was tested under wine, and it
seems to work correctly, but it was only tested with a single device.
In attachment is the patch.

If this patch is acceptable, then the next step is be to implement a
windows XInput joystick and haptics driver. After that will be
Android. I really don't have a OSX system so I cannot make the prort
to that system or OSX, so I was hoping that at least for that platform
someone would step up and help out. Everyone else can test this patch
on windows (using ex_haptic and ex_haptic2) and report any bugs or
suggestions for improvement.

Kind Regards,

B.
From 9a267bef6ad5d92994ca8f0b61ee9269b588ddaa Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Sat, 26 Apr 2014 01:34:51 +0200
Subject: [PATCH 01/15] Added simpler capability checking and autocenter
 functions to the haprics API. This is because they
 are useful and supported both in Linux and in the new
 Windows driver.

---
 docs/src/refman/haptic.txt                 |   46 +++++++++++++++++++++++++++-
 include/allegro5/haptic.h                  |   19 ++++++++----
 include/allegro5/internal/aintern_haptic.h |    5 ++-
 src/haptic.c                               |   28 +++++++++++++++++
 src/linux/lhaptic.c                        |   42 ++++++++++++++++++++++++-
 5 files changed, 131 insertions(+), 9 deletions(-)

diff --git a/docs/src/refman/haptic.txt b/docs/src/refman/haptic.txt
index 18cac76..75a164b 100644
--- a/docs/src/refman/haptic.txt
+++ b/docs/src/refman/haptic.txt
@@ -327,7 +327,8 @@ Since: 5.1.8
 
 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.
+the joystick configuration changes, such as through hot plugging. In that case,
+the old haptic device must be released using [al_release_haptic].
 
 Since: 5.1.8
 
@@ -359,6 +360,16 @@ if set, indicate that the haptic device supports the given feature.
 
 Since: 5.1.8
 
+## API: al_is_haptic_capable
+
+Returns true if the haptic device is supports the feature indicated by 
+the query parameter, false if the feature is not supported. 
+The query parameter must be one of the values of [ALLEGRO_HAPTIC_CONSTANTS]. 
+
+Since: 5.1.9
+
+See also: [al_get_haptic_capabilities]
+
 ## API: al_set_haptic_gain
 
 Sets the gain of the haptic device if supported. Gain is much like volume for
@@ -381,6 +392,39 @@ gain influence.
 
 Since: 5.1.8
 
+## API: al_set_haptic_autocenter
+
+Turns on or off the automatic centering feature of the haptic device if 
+supported. Depending on the device automatic centering may ensure that the 
+axes of the device are  centered again automatically after playing 
+a haptic effect. The intensity parameter should be passed with a value 
+between 0.0 and 1.0. The value 0.0 means automatic centering is 
+disabled, and 1.0 means full strength automatic centering. Any value
+in between those two extremes will result in partial automatic 
+centering.
+Returns true if set sucessfully, false if not.
+Can only work if [al_get_haptic_capabilities] returns a value that has
+[ALLEGRO_HAPTIC_AUTOCENTER] set.  If not, this function returns false.
+
+Since: 5.1.9
+
+## API: al_get_haptic_autocenter
+
+Returns the current automatic centering intensity of the device. 
+Depending on the device automatic centering may ensure that the 
+axes of the device are  centered again automatically after playing 
+a haptic effect. The return value can be between 0.0 and 1.0. 
+The value 0.0 means automatic centering is disabled, and 1.0 means 
+automatic centering is enabled at full strength. Any value
+in between those two extremes means partial automatic 
+centering is enabled.
+Can only work if [al_get_haptic_capabilities] returns a 
+value that has [ALLEGRO_HAPTIC_AUTOCENTER] set. If not, this function 
+returns 0.0.
+
+Since: 5.1.9
+
+
 ## API: al_get_num_haptic_effects
 
 Returns the maximum amount of haptic effects that can be uploaded to the
diff --git a/include/allegro5/haptic.h b/include/allegro5/haptic.h
index 58302d1..1db0aa5 100644
--- a/include/allegro5/haptic.h
+++ b/include/allegro5/haptic.h
@@ -52,7 +52,8 @@ enum ALLEGRO_HAPTIC_CONSTANTS
    ALLEGRO_HAPTIC_GAIN     = 1 << 14,
    ALLEGRO_HAPTIC_ANGLE    = 1 << 15,
    ALLEGRO_HAPTIC_RADIUS   = 1 << 16,
-   ALLEGRO_HAPTIC_AZIMUTH  = 1 << 17
+   ALLEGRO_HAPTIC_AZIMUTH  = 1 << 17,
+   ALLEGRO_HAPTIC_AUTOCENTER= 1 << 18,
 };
 
 
@@ -212,18 +213,24 @@ AL_FUNC(bool, al_release_haptic, (ALLEGRO_HAPTIC *));
 
 AL_FUNC(bool, al_get_haptic_active, (ALLEGRO_HAPTIC *));
 AL_FUNC(int, al_get_haptic_capabilities, (ALLEGRO_HAPTIC *));
-AL_FUNC(bool, al_set_haptic_gain, (ALLEGRO_HAPTIC *, double gain));
+AL_FUNC(bool, al_is_haptic_capable, (ALLEGRO_HAPTIC *, int));
+
+AL_FUNC(bool, al_set_haptic_gain, (ALLEGRO_HAPTIC *, double));
 AL_FUNC(double, al_get_haptic_gain, (ALLEGRO_HAPTIC *));
 
+AL_FUNC(bool, al_set_haptic_autocenter, (ALLEGRO_HAPTIC *, double));
+AL_FUNC(double, al_get_haptic_autocenter, (ALLEGRO_HAPTIC *));
+
+
 AL_FUNC(int, al_get_num_haptic_effects, (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 *, ALLEGRO_HAPTIC_EFFECT_ID *play_id));
-AL_FUNC(bool, al_play_haptic_effect, (ALLEGRO_HAPTIC_EFFECT_ID *, int loop));
-AL_FUNC(bool, al_upload_and_play_haptic_effect, (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, int loop, ALLEGRO_HAPTIC_EFFECT_ID *play_id));
+AL_FUNC(bool, al_upload_haptic_effect, (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, ALLEGRO_HAPTIC_EFFECT_ID *));
+AL_FUNC(bool, al_play_haptic_effect, (ALLEGRO_HAPTIC_EFFECT_ID *, int));
+AL_FUNC(bool, al_upload_and_play_haptic_effect, (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, int, ALLEGRO_HAPTIC_EFFECT_ID *));
 AL_FUNC(bool, al_stop_haptic_effect, (ALLEGRO_HAPTIC_EFFECT_ID *));
 AL_FUNC(bool, al_is_haptic_effect_playing, (ALLEGRO_HAPTIC_EFFECT_ID *));
 AL_FUNC(bool, al_release_haptic_effect, (ALLEGRO_HAPTIC_EFFECT_ID *));
-AL_FUNC(bool, al_rumble_haptic, (ALLEGRO_HAPTIC *, double intensity, double duration, ALLEGRO_HAPTIC_EFFECT_ID *));
+AL_FUNC(bool, al_rumble_haptic, (ALLEGRO_HAPTIC *, double, double, ALLEGRO_HAPTIC_EFFECT_ID *));
 
 
 #ifdef __cplusplus
diff --git a/include/allegro5/internal/aintern_haptic.h b/include/allegro5/internal/aintern_haptic.h
index e21532d..afaefeb 100644
--- a/include/allegro5/internal/aintern_haptic.h
+++ b/include/allegro5/internal/aintern_haptic.h
@@ -46,6 +46,8 @@ typedef struct ALLEGRO_HAPTIC_DRIVER
    AL_METHOD(bool, is_effect_playing, (ALLEGRO_HAPTIC_EFFECT_ID *));
    AL_METHOD(bool, release_effect, (ALLEGRO_HAPTIC_EFFECT_ID *));
    AL_METHOD(bool, release, (ALLEGRO_HAPTIC *));
+   AL_METHOD(double, get_autocenter, (ALLEGRO_HAPTIC *));
+   AL_METHOD(bool, set_autocenter, (ALLEGRO_HAPTIC *, double));  
 } ALLEGRO_HAPTIC_DRIVER;
 
 
@@ -61,8 +63,9 @@ enum ALLEGRO_HAPTIC_PARENT {
 struct ALLEGRO_HAPTIC
 {
    enum ALLEGRO_HAPTIC_PARENT from;
-   void *device;
+   void * device;
    double gain;
+   double autocenter;
 };
 
 
diff --git a/src/haptic.c b/src/haptic.c
index d9c7922..83c3969 100644
--- a/src/haptic.c
+++ b/src/haptic.c
@@ -199,6 +199,12 @@ int al_get_haptic_capabilities(ALLEGRO_HAPTIC *hap)
    return haptic_driver->get_capabilities(hap);
 }
 
+/* Function: al_is_haptic_capable
+ */ 
+bool al_is_haptic_capable(ALLEGRO_HAPTIC * hap, int query) {
+  int capabilities = al_get_haptic_capabilities(hap);
+  return (capabilities & query) == query;
+}
 
 /* Function: al_get_haptic_gain
  */
@@ -221,6 +227,28 @@ bool al_set_haptic_gain(ALLEGRO_HAPTIC *hap, double gain)
    return haptic_driver->set_gain(hap, gain);
 }
 
+/* Function: al_get_haptic_autocenter
+ */
+double al_get_haptic_autocenter(ALLEGRO_HAPTIC *hap)
+{
+   ASSERT(hap);
+   ASSERT(haptic_driver);
+
+   return haptic_driver->get_autocenter(hap);
+}
+
+
+/* Function: al_set_haptic_autocenter
+ */
+bool al_set_haptic_autocenter(ALLEGRO_HAPTIC *hap, double intensity)
+{
+   ASSERT(hap);
+   ASSERT(haptic_driver);
+   
+   return haptic_driver->set_autocenter(hap, intensity);
+}
+
+
 
 /* Function: al_get_num_haptic_effects
  */
diff --git a/src/linux/lhaptic.c b/src/linux/lhaptic.c
index 88a252a..4af4c13 100644
--- a/src/linux/lhaptic.c
+++ b/src/linux/lhaptic.c
@@ -89,6 +89,8 @@ static bool lhap_stop_effect(ALLEGRO_HAPTIC_EFFECT_ID *id);
 static bool lhap_is_effect_playing(ALLEGRO_HAPTIC_EFFECT_ID *id);
 static bool lhap_release_effect(ALLEGRO_HAPTIC_EFFECT_ID *id);
 
+static double lhap_get_autocenter(ALLEGRO_HAPTIC *dev);
+static double lhap_set_autocenter(ALLEGRO_HAPTIC *dev, double);
 
 ALLEGRO_HAPTIC_DRIVER _al_hapdrv_linux =
 {
@@ -124,7 +126,10 @@ ALLEGRO_HAPTIC_DRIVER _al_hapdrv_linux =
    lhap_is_effect_playing,
    lhap_release_effect,
 
-   lhap_release
+   lhap_release,
+   
+   lhap_get_autocenter,
+   lhap_set_autocenter
 };
 
 
@@ -153,6 +158,7 @@ static const struct CAP_MAP cap_map[] = {
    { FF_SAW_DOWN, ALLEGRO_HAPTIC_SAW_DOWN },
    { FF_CUSTOM,   ALLEGRO_HAPTIC_CUSTOM },
    { FF_GAIN,     ALLEGRO_HAPTIC_GAIN },
+   { FF_AUTOCENTER, ALLEGRO_HAPTIC_AUTOCENTER },
    { -1,          -1 }
 };
 
@@ -598,6 +604,9 @@ static double lhap_get_gain(ALLEGRO_HAPTIC *dev)
 {
    ALLEGRO_HAPTIC_LINUX *lhap = lhap_from_al(dev);
    (void)dev;
+   if(!al_is_haptic_capable(hap, ALLEGRO_HAPTIC_GAIN)) { 
+     return 0.0;  
+   } 
 
    /* Unfortunately there seems to be no API to GET gain, only to set?!
     * So, return the stored gain.
@@ -623,6 +632,37 @@ static bool lhap_set_gain(ALLEGRO_HAPTIC *dev, double gain)
 }
 
 
+static bool lhap_set_autocenter(ALLEGRO_HAPTIC *dev, double autocenter)
+{
+   ALLEGRO_HAPTIC_LINUX *lhap = lhap_from_al(dev);
+   struct input_event ie;
+
+   lhap->parent.autocenter = autocenter;
+   timerclear(&ie.time);
+   ie.type = EV_FF;
+   ie.code = FF_AUTOCENTER;
+   ie.value = (__s32) ((double)0xFFFF * gain);
+   if (write(lhap->fd, &ie, sizeof(ie)) < 0) {
+      return false;
+   }
+   return true;
+}
+
+static double lhap_get_autocenter(ALLEGRO_HAPTIC *dev)
+{
+   ALLEGRO_HAPTIC_LINUX *lhap = lhap_from_al(dev);
+   (void)dev;
+   
+   if(!al_is_haptic_capable(hap, ALLEGRO_HAPTIC_AUTOCENTER)) { 
+     return 0.0;
+   }
+
+   /* Unfortunately there seems to be no API to GET gain, only to set?!
+    * So, return the stored autocenter.
+    */
+   return lhap->parent.autocenter;
+}
+
 int lhap_get_num_effects(ALLEGRO_HAPTIC *dev)
 {
    ALLEGRO_HAPTIC_LINUX *lhap = lhap_from_al(dev);
-- 
1.7.10.4


From 79128ac2dd4c7209a3675afa26864d38e298eb80 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Sat, 26 Apr 2014 01:34:51 +0200
Subject: [PATCH 02/15] Exposing part of the Windows joystick driver for use
 by the Windows haptic driver.

---
 include/allegro5/internal/aintern_wjoydxnu.h |   84 ++++++++++++++++++++++++++
 src/win/wjoydxnu.cpp                         |   77 ++---------------------
 2 files changed, 88 insertions(+), 73 deletions(-)
 create mode 100644 include/allegro5/internal/aintern_wjoydxnu.h

diff --git a/include/allegro5/internal/aintern_wjoydxnu.h b/include/allegro5/internal/aintern_wjoydxnu.h
new file mode 100644
index 0000000..5a0c7bb
--- /dev/null
+++ b/include/allegro5/internal/aintern_wjoydxnu.h
@@ -0,0 +1,84 @@
+#ifndef __al_included_allegro_aintern_wjoydxnu_h
+#define __al_included_allegro_aintern_wjoydxnu_h
+
+
+/** Part of the Windows DirectInput joystick 
+ * types are shared here for use by the haptic susbystem. */
+
+/* arbitrary limit to make life easier; this was the limit in Allegro 4.1.x */
+#define MAX_JOYSTICKS        8
+
+/* these limits are from DIJOYSTICK_STATE in dinput.h */
+#define MAX_SLIDERS          2
+#define MAX_POVS             4
+#define MAX_BUTTONS          32
+
+/* the number of joystick events that DirectInput is told to buffer */
+#define DEVICE_BUFFER_SIZE   10
+
+/* make sure all the constants add up */
+/* the first two sticks are (x,y,z) and (rx,ry,rz) */
+ALLEGRO_STATIC_ASSERT(wjoydxnu, _AL_MAX_JOYSTICK_STICKS >= (2 + MAX_SLIDERS + MAX_POVS));
+ALLEGRO_STATIC_ASSERT(wjoydxnu, _AL_MAX_JOYSTICK_BUTTONS >= MAX_BUTTONS);
+
+
+#define GUID_EQUAL(a, b)     (0 == memcmp(&(a), &(b), sizeof(GUID)))
+
+
+typedef enum {
+   STATE_UNUSED,
+   STATE_BORN,
+   STATE_ALIVE,
+   STATE_DYING
+} CONFIG_STATE;
+
+#define ACTIVE_STATE(st) \
+   ((st) == STATE_ALIVE || (st) == STATE_DYING)
+
+
+/* helper structure to record information through object_enum_callback */
+#define NAME_LEN     128
+
+typedef struct {
+   bool have_x;      char name_x[NAME_LEN];
+   bool have_y;      char name_y[NAME_LEN];
+   bool have_z;      char name_z[NAME_LEN];
+   bool have_rx;     char name_rx[NAME_LEN];
+   bool have_ry;     char name_ry[NAME_LEN];
+   bool have_rz;     char name_rz[NAME_LEN];
+   int num_sliders;  char name_slider[MAX_SLIDERS][NAME_LEN];
+   int num_povs;     char name_pov[MAX_POVS][NAME_LEN];
+   int num_buttons;  char name_button[MAX_BUTTONS][NAME_LEN];
+} CAPS_AND_NAMES;
+
+
+/* map a DirectInput axis to an Allegro (stick,axis) pair */
+typedef struct {
+   int stick, axis;
+} AXIS_MAPPING;
+
+
+typedef struct ALLEGRO_JOYSTICK_DIRECTX {
+   ALLEGRO_JOYSTICK parent;          /* must be first */
+   CONFIG_STATE config_state;
+   bool marked;
+   LPDIRECTINPUTDEVICE2 device;
+   GUID guid;
+   HANDLE waker_event;
+
+   ALLEGRO_JOYSTICK_STATE joystate;
+   AXIS_MAPPING x_mapping;
+   AXIS_MAPPING y_mapping;
+   AXIS_MAPPING z_mapping;
+   AXIS_MAPPING rx_mapping;
+   AXIS_MAPPING ry_mapping;
+   AXIS_MAPPING rz_mapping;
+   AXIS_MAPPING slider_mapping[MAX_SLIDERS];
+   int pov_mapping_stick[MAX_POVS];
+   char name[80];
+   char all_names[512]; /* button/stick/axis names with NUL terminators */
+} ALLEGRO_JOYSTICK_DIRECTX;
+
+#endif
+
+/* vim: set sts=3 sw=3 et: */
diff --git a/src/win/wjoydxnu.cpp b/src/win/wjoydxnu.cpp
index 8fd0eda..cb5de9c 100644
--- a/src/win/wjoydxnu.cpp
+++ b/src/win/wjoydxnu.cpp
@@ -82,79 +82,10 @@
 
 ALLEGRO_DEBUG_CHANNEL("dinput")
 
-/* arbitrary limit to make life easier; this was the limit in Allegro 4.1.x */
-#define MAX_JOYSTICKS        8
-
-/* these limits are from DIJOYSTICK_STATE in dinput.h */
-#define MAX_SLIDERS          2
-#define MAX_POVS             4
-#define MAX_BUTTONS          32
-
-/* the number of joystick events that DirectInput is told to buffer */
-#define DEVICE_BUFFER_SIZE   10
-
-/* make sure all the constants add up */
-/* the first two sticks are (x,y,z) and (rx,ry,rz) */
-ALLEGRO_STATIC_ASSERT(wjoydxnu, _AL_MAX_JOYSTICK_STICKS >= (2 + MAX_SLIDERS + MAX_POVS));
-ALLEGRO_STATIC_ASSERT(wjoydxnu, _AL_MAX_JOYSTICK_BUTTONS >= MAX_BUTTONS);
-
-
-#define GUID_EQUAL(a, b)     (0 == memcmp(&(a), &(b), sizeof(GUID)))
-
-
-typedef enum {
-   STATE_UNUSED,
-   STATE_BORN,
-   STATE_ALIVE,
-   STATE_DYING
-} CONFIG_STATE;
-
-#define ACTIVE_STATE(st) \
-   ((st) == STATE_ALIVE || (st) == STATE_DYING)
-
-
-/* helper structure to record information through object_enum_callback */
-#define NAME_LEN     128
-
-typedef struct {
-   bool have_x;      char name_x[NAME_LEN];
-   bool have_y;      char name_y[NAME_LEN];
-   bool have_z;      char name_z[NAME_LEN];
-   bool have_rx;     char name_rx[NAME_LEN];
-   bool have_ry;     char name_ry[NAME_LEN];
-   bool have_rz;     char name_rz[NAME_LEN];
-   int num_sliders;  char name_slider[MAX_SLIDERS][NAME_LEN];
-   int num_povs;     char name_pov[MAX_POVS][NAME_LEN];
-   int num_buttons;  char name_button[MAX_BUTTONS][NAME_LEN];
-} CAPS_AND_NAMES;
-
-
-/* map a DirectInput axis to an Allegro (stick,axis) pair */
-typedef struct {
-   int stick, axis;
-} AXIS_MAPPING;
-
-
-typedef struct ALLEGRO_JOYSTICK_DIRECTX {
-   ALLEGRO_JOYSTICK parent;          /* must be first */
-   CONFIG_STATE config_state;
-   bool marked;
-   LPDIRECTINPUTDEVICE2 device;
-   GUID guid;
-   HANDLE waker_event;
-
-   ALLEGRO_JOYSTICK_STATE joystate;
-   AXIS_MAPPING x_mapping;
-   AXIS_MAPPING y_mapping;
-   AXIS_MAPPING z_mapping;
-   AXIS_MAPPING rx_mapping;
-   AXIS_MAPPING ry_mapping;
-   AXIS_MAPPING rz_mapping;
-   AXIS_MAPPING slider_mapping[MAX_SLIDERS];
-   int pov_mapping_stick[MAX_POVS];
-   char name[80];
-   char all_names[512]; /* button/stick/axis names with NUL terminators */
-} ALLEGRO_JOYSTICK_DIRECTX;
+#include "allegro5/joystick.h"
+#include "allegro5/internal/aintern_joystick.h"
+#include "allegro5/internal/aintern_wjoydxnu.h"
+
 
 
 
-- 
1.7.10.4


From d582497196d65af66cd52a644f1765e161e35473 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Sat, 26 Apr 2014 01:34:52 +0200
Subject: [PATCH 03/15] Adding Windows haptic driver

---
 cmake/FileList.cmake              |    2 +
 include/allegro5/platform/alwin.h |    3 +
 src/win/whaptic.c                 |   29 ++
 src/win/whaptic.cpp               |  909 +++++++++++++++++++++++++++++++++++++
 4 files changed, 943 insertions(+)
 create mode 100644 src/win/whaptic.c
 create mode 100644 src/win/whaptic.cpp

diff --git a/cmake/FileList.cmake b/cmake/FileList.cmake
index af72772..9e4de24 100644
--- a/cmake/FileList.cmake
+++ b/cmake/FileList.cmake
@@ -53,6 +53,8 @@ set(ALLEGRO_SRC_FILES
     )
 
 set(ALLEGRO_SRC_WIN_FILES
+    src/win/whaptic.c
+    src/win/whaptic.cpp
     src/win/wjoydrv.c
     src/win/wjoydxnu.cpp
     src/win/wkeyboard.c
diff --git a/include/allegro5/platform/alwin.h b/include/allegro5/platform/alwin.h
index ed2a136..d8f3a07 100644
--- a/include/allegro5/platform/alwin.h
+++ b/include/allegro5/platform/alwin.h
@@ -62,3 +62,6 @@ AL_VAR(struct ALLEGRO_JOYSTICK_DRIVER, _al_joydrv_directx);
 #define _AL_JOYSTICK_DRIVER_DIRECTX                                     \
    { AL_JOY_TYPE_DIRECTX,  &_al_joydrv_directx,    true  },
 
+
+/** HAPTIC driver ID */
+#define _ALLEGRO_HAPDRV_WINDOWS    AL_ID('W','I','N','H')
diff --git a/src/win/whaptic.c b/src/win/whaptic.c
new file mode 100644
index 0000000..ca88815
--- /dev/null
+++ b/src/win/whaptic.c
@@ -0,0 +1,29 @@
+/*         ______   ___    ___ 
+ *        /\  _  \ /\_ \  /\_ \ 
+ *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
+ *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
+ *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
+ *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
+ *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
+ *                                           /\____/
+ *                                           \_/__/
+ *
+ *      List of Windows joystick drivers, kept in a seperate file so that
+ *      they can be overriden by user programs.
+ *
+ *      By Shawn Hargreaves.
+ *
+ *      See readme.txt for copyright information.
+ */
+
+
+#include "allegro5/allegro.h"
+#include "allegro5/internal/aintern.h"
+#include "allegro5/internal/aintern_haptic.h"
+
+#ifndef ALLEGRO_WINDOWS
+#error something is wrong with the makefile
+#endif
+
+
+
diff --git a/src/win/whaptic.cpp b/src/win/whaptic.cpp
new file mode 100644
index 0000000..2ff7547
--- /dev/null
+++ b/src/win/whaptic.cpp
@@ -0,0 +1,909 @@
+/*         ______   ___    ___
+ *        /\  _  \ /\_ \  /\_ \
+ *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
+ *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
+ *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
+ *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
+ *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
+ *                                           /\____/
+ *                                           \_/__/
+ *
+ *      Windows haptic (force-feedback) device driver.
+ *
+ *      By Beoran.
+ *
+ *      See LICENSE.txt for copyright information.
+ */
+
+
+
+#define ALLEGRO_NO_COMPATIBILITY
+
+#define DIRECTINPUT_VERSION 0x0800
+
+/* For waitable timers */
+#define _WIN32_WINNT 0x400
+
+#include "allegro5/allegro.h"
+#include "allegro5/internal/aintern.h"
+#include "allegro5/platform/aintwin.h"
+#include "allegro5/internal/aintern_haptic.h"
+#include "allegro5/internal/aintern_events.h"
+#include "allegro5/internal/aintern_joystick.h"
+#include "allegro5/internal/aintern_bitmap.h"
+
+#ifndef ALLEGRO_WINDOWS
+#error something is wrong with the makefile
+#endif
+
+#ifdef ALLEGRO_MINGW32
+   #undef MAKEFOURCC
+#endif
+
+#include <initguid.h>
+#include <stdio.h>
+#include <mmsystem.h>
+#include <process.h>
+#include <math.h>
+#include <dinput.h>
+/* #include <sys/time.h> */
+
+#include "allegro5/internal/aintern_wjoydxnu.h"
+
+ALLEGRO_DEBUG_CHANNEL("whaptic")
+
+/* Support at most 32 haptic devices. */
+#define HAPTICS_MAX             32
+
+/* Support at most 16 effects per device. */
+#define HAPTICS_EFFECTS_MAX     16
+
+/* Support at most 3 axes per device. */
+#define HAPTICS_AXES_MAX        3
+
+/*
+ * Haptic system effect data.
+ */
+typedef struct 
+{
+   bool                active;
+   DIEFFECT            effect;
+   LPDIRECTINPUTEFFECT ref;
+   /* XINPUT_VIBRATION vibration; */
+} ALLEGRO_HAPTIC_EFFECT_WINDOWS ;
+
+
+
+typedef struct
+{
+   struct ALLEGRO_HAPTIC parent; /* must be first */
+   bool active;
+   LPDIRECTINPUTDEVICE2 device;
+   GUID                 guid;
+   DIDEVICEINSTANCE     instance;
+   DIDEVCAPS            capabilities;
+   LPDIRECTINPUTDEVICE8 device8;
+
+   
+   int flags;
+   ALLEGRO_HAPTIC_EFFECT_WINDOWS effects[HAPTICS_EFFECTS_MAX];
+   DWORD axes[HAPTICS_AXES_MAX];
+   int naxes;
+  
+} ALLEGRO_HAPTIC_WINDOWS;
+
+
+#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) % LONG_BITS)) & (addr)[(nr) / LONG_BITS])
+
+
+/* forward declarations */
+static bool whap_init_haptic(void);
+static void whap_exit_haptic(void);
+
+static bool whap_is_mouse_haptic(ALLEGRO_MOUSE *dev);
+static bool whap_is_joystick_haptic(ALLEGRO_JOYSTICK *);
+static bool whap_is_keyboard_haptic(ALLEGRO_KEYBOARD *dev);
+static bool whap_is_display_haptic(ALLEGRO_DISPLAY *dev);
+static bool whap_is_touch_input_haptic(ALLEGRO_TOUCH_INPUT *dev);
+
+static ALLEGRO_HAPTIC *whap_get_from_mouse(ALLEGRO_MOUSE *dev);
+static ALLEGRO_HAPTIC *whap_get_from_joystick(ALLEGRO_JOYSTICK *dev);
+static ALLEGRO_HAPTIC *whap_get_from_keyboard(ALLEGRO_KEYBOARD *dev);
+static ALLEGRO_HAPTIC *whap_get_from_display(ALLEGRO_DISPLAY *dev);
+static ALLEGRO_HAPTIC *whap_get_from_touch_input(ALLEGRO_TOUCH_INPUT *dev);
+
+static bool whap_release(ALLEGRO_HAPTIC *haptic);
+
+static bool whap_get_active(ALLEGRO_HAPTIC *hap);
+static int whap_get_capabilities(ALLEGRO_HAPTIC *dev);
+static double whap_get_gain(ALLEGRO_HAPTIC *dev);
+static bool whap_set_gain(ALLEGRO_HAPTIC *dev, double);
+static int whap_get_num_effects(ALLEGRO_HAPTIC *dev);
+
+static bool whap_is_effect_ok(ALLEGRO_HAPTIC *dev, ALLEGRO_HAPTIC_EFFECT *eff);
+static bool whap_upload_effect(ALLEGRO_HAPTIC *dev,
+                               ALLEGRO_HAPTIC_EFFECT *eff,
+                               ALLEGRO_HAPTIC_EFFECT_ID *id);
+static bool whap_play_effect(ALLEGRO_HAPTIC_EFFECT_ID *id, int loop);
+static bool whap_stop_effect(ALLEGRO_HAPTIC_EFFECT_ID *id);
+static bool whap_is_effect_playing(ALLEGRO_HAPTIC_EFFECT_ID *id);
+static bool whap_release_effect(ALLEGRO_HAPTIC_EFFECT_ID *id);
+
+static double whap_get_autocenter(ALLEGRO_HAPTIC *dev);
+static bool whap_set_autocenter(ALLEGRO_HAPTIC *dev, double);
+
+ALLEGRO_HAPTIC_DRIVER _al_hapdrv_windows =
+{
+   _ALLEGRO_HAPDRV_WINDOWS,
+   "",
+   "",
+   "Windows haptic(s)",
+   whap_init_haptic,
+   whap_exit_haptic,
+
+   whap_is_mouse_haptic,
+   whap_is_joystick_haptic,
+   whap_is_keyboard_haptic,
+   whap_is_display_haptic,
+   whap_is_touch_input_haptic,
+
+   whap_get_from_mouse,
+   whap_get_from_joystick,
+   whap_get_from_keyboard,
+   whap_get_from_display,
+   whap_get_from_touch_input,
+
+   whap_get_active,
+   whap_get_capabilities,
+   whap_get_gain,
+   whap_set_gain,
+   whap_get_num_effects,
+
+   whap_is_effect_ok,
+   whap_upload_effect,
+   whap_play_effect,
+   whap_stop_effect,
+   whap_is_effect_playing,
+   whap_release_effect,
+
+   whap_release,
+   
+   whap_get_autocenter,
+   whap_set_autocenter
+};
+
+
+static ALLEGRO_HAPTIC_WINDOWS haptics[HAPTICS_MAX];
+static ALLEGRO_MUTEX *haptic_mutex = NULL;
+
+/* Capability map between directinput effects and allegro effect types. */
+struct CAP_MAP {
+   GUID guid;
+   int allegro_bit;
+};
+
+/* GUID values are borrowed from Wine */
+#define DEFINE_PRIVATE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+   static const GUID name = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }
+
+DEFINE_PRIVATE_GUID(_al_GUID_None, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 
+   
+
+static const struct CAP_MAP cap_map[] = {
+   { { GUID_ConstantForce },    ALLEGRO_HAPTIC_CONSTANT },
+   { { GUID_Spring },           ALLEGRO_HAPTIC_SPRING },
+   { { GUID_Spring },           ALLEGRO_HAPTIC_FRICTION },
+   { { GUID_Damper },           ALLEGRO_HAPTIC_DAMPER },
+   { { GUID_Inertia },          ALLEGRO_HAPTIC_INERTIA },
+   { { GUID_RampForce },        ALLEGRO_HAPTIC_RAMP },
+   { { GUID_Square },           ALLEGRO_HAPTIC_SQUARE },
+   { { GUID_Triangle },         ALLEGRO_HAPTIC_TRIANGLE },
+   { { GUID_Sine },             ALLEGRO_HAPTIC_SINE },
+   { { GUID_SawtoothUp },       ALLEGRO_HAPTIC_SAW_UP },
+   { { GUID_SawtoothDown },     ALLEGRO_HAPTIC_SAW_DOWN },
+   { { GUID_CustomForce },      ALLEGRO_HAPTIC_CUSTOM },
+   { { _al_GUID_None    },      -1 }
+};
+
+
+static bool whap_init_haptic(void)
+{
+   int i;
+
+   ASSERT(haptic_mutex == NULL);
+   haptic_mutex = al_create_mutex();
+   if (!haptic_mutex)
+      return false;
+
+   for (i = 0; i < HAPTICS_MAX; i++) {
+      haptics[i].active = false;
+   }
+
+   return true;
+}
+
+
+static ALLEGRO_HAPTIC_WINDOWS *whap_get_available_haptic(void)
+{
+   int i;
+
+   for (i = 0; i < HAPTICS_MAX; i++) {
+      if (!haptics[i].active) {
+         haptics[i].active = true;
+         return &haptics[i];
+      }
+   }
+
+   return NULL;
+}
+
+
+/* Converts a generic haptic device to a Windows-specific one. */
+static ALLEGRO_HAPTIC_WINDOWS *whap_from_al(ALLEGRO_HAPTIC *hap)
+{
+   return (ALLEGRO_HAPTIC_WINDOWS *) hap;
+}
+
+
+static void whap_exit_haptic(void)
+{
+   ASSERT(haptic_mutex);
+   al_destroy_mutex(haptic_mutex);
+   haptic_mutex = NULL;
+}
+
+#ifdef _COMMENT_
+static bool whap_type2win(__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 whap_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 whap_time2lin(__u16 *res, double sec)
+{
+   ASSERT(res);
+
+   if (sec < 0.0 || sec > 32.767)
+      return false;
+   (*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 whap_stime2lin(__s16 *res, double sec)
+{
+   ASSERT(res);
+
+   if (sec < -32.767 || sec > 32.767)
+      return false;
+   (*res) = (__s16) round(sec * 1000.0);
+   return true;
+}
+
+
+/* Converts replay data to Linux-compatible data. */
+static bool whap_replay2lin(struct ff_replay *lin,
+   struct ALLEGRO_HAPTIC_REPLAY *al)
+{
+   return whap_time2lin(&lin->delay, al->delay)
+      && whap_time2lin(&lin->length, al->length);
+}
+
+
+/* Converts the level in range 0.0 to 1.0 to a Linux-compatible level.
+ * Returns false if out of bounds.
+ */
+static bool whap_level2lin(__u16 *res, double level)
+{
+   ASSERT(res);
+
+   if (level < 0.0 || level > 1.0)
+      return false;
+   *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 whap_slevel2lin(__s16 *res, double level)
+{
+   ASSERT(res);
+
+   if (level < -1.0 || level > 1.0)
+      return false;
+   *res = (__s16) round(level * (double)0x7ffe);
+   return true;
+}
+
+
+/* Converts an Allegro haptic effect envelope to Linux input API. */
+static bool whap_envelope2lin(struct ff_envelope *lin,
+   struct ALLEGRO_HAPTIC_ENVELOPE *al)
+{
+   return whap_time2lin(&lin->attack_length, al->attack_length)
+      && whap_time2lin(&lin->fade_length, al->fade_length)
+      && whap_level2lin(&lin->attack_level, al->attack_level)
+      && whap_level2lin(&lin->fade_level, al->fade_level);
+}
+
+
+/* Converts a rumble effect to Linux input API. */
+static bool whap_rumble2lin(struct ff_rumble_effect *lin,
+   struct ALLEGRO_HAPTIC_RUMBLE_EFFECT *al)
+{
+   return whap_level2lin(&lin->strong_magnitude, al->strong_magnitude)
+      && whap_level2lin(&lin->weak_magnitude, al->weak_magnitude);
+}
+
+
+/* Converts a constant effect to Linux input API. */
+static bool whap_constant2lin(struct ff_constant_effect *lin,
+   struct ALLEGRO_HAPTIC_CONSTANT_EFFECT *al)
+{
+   return whap_envelope2lin(&lin->envelope, &al->envelope)
+      && whap_slevel2lin(&lin->level, al->level);
+}
+
+
+/* Converts a ramp effect to Linux input API. */
+static bool whap_ramp2lin(struct ff_ramp_effect *lin,
+   struct ALLEGRO_HAPTIC_RAMP_EFFECT *al)
+{
+   return whap_envelope2lin(&lin->envelope, &al->envelope)
+      && whap_slevel2lin(&lin->start_level, al->start_level)
+      && whap_slevel2lin(&lin->end_level, al->end_level);
+}
+
+
+/* Converts a ramp effect to Linux input API. */
+static bool whap_condition2lin(struct ff_condition_effect *lin,
+   struct ALLEGRO_HAPTIC_CONDITION_EFFECT *al)
+{
+   return whap_slevel2lin(&lin->center, al->center)
+      && whap_level2lin(&lin->deadband, al->deadband)
+      && whap_slevel2lin(&lin->right_coeff, al->right_coeff)
+      && whap_level2lin(&lin->right_saturation, al->right_saturation)
+      && whap_slevel2lin(&lin->left_coeff, al->left_coeff)
+      && whap_level2lin(&lin->left_saturation, al->left_saturation);
+}
+
+
+/* Converts a periodic effect to linux input API. */
+static bool whap_periodic2lin(struct ff_periodic_effect *lin,
+   struct ALLEGRO_HAPTIC_PERIODIC_EFFECT *al)
+{
+   /* Custom data is not supported yet, because currently no Linux
+    * haptic driver supports it.
+    */
+   if (al->custom_data)
+      return false;
+
+   return whap_slevel2lin(&lin->magnitude, al->magnitude)
+      && whap_stime2lin(&lin->offset, al->offset)
+      && whap_time2lin(&lin->period, al->period)
+      && whap_time2lin(&lin->phase, al->phase)
+      && whap_wave2lin(&lin->waveform, al->waveform)
+      && whap_envelope2lin(&lin->envelope, &al->envelope);
+}
+
+
+/* Converts Allegro haptic effect to Linux input API. */
+static bool whap_effect2lin(struct ff_effect *lin, ALLEGRO_HAPTIC_EFFECT *al)
+{
+   memset(lin, 0, sizeof(*lin));
+
+   if (!whap_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 (!whap_replay2lin(&lin->replay, &al->replay))
+      return false;
+   switch (lin->type) {
+      case FF_RUMBLE:
+         return whap_rumble2lin(&lin->u.rumble, &al->data.rumble);
+      case FF_PERIODIC:
+         return whap_periodic2lin(&lin->u.periodic, &al->data.periodic);
+      case FF_CONSTANT:
+         return whap_constant2lin(&lin->u.constant, &al->data.constant);
+      case FF_RAMP:
+         return whap_ramp2lin(&lin->u.ramp, &al->data.ramp);
+      case FF_SPRING:   /* fall through */
+      case FF_FRICTION: /* fall through */
+      case FF_DAMPER:   /* fall through */
+      case FF_INERTIA:
+         return whap_condition2lin(&lin->u.condition[0], &al->data.condition);
+      default:
+         return false;
+   }
+}
+
+#endif // __COMMENT__
+
+static bool whap_get_active(ALLEGRO_HAPTIC *haptic)
+{
+   ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(haptic);
+   return whap->active;
+}
+
+
+static bool whap_is_mouse_haptic(ALLEGRO_MOUSE *mouse)
+{
+   (void)mouse;
+   return false;
+}
+
+
+static bool whap_is_joystick_haptic(ALLEGRO_JOYSTICK *joy)
+{
+   ALLEGRO_JOYSTICK_DIRECTX *joydx = (ALLEGRO_JOYSTICK_DIRECTX *) joy;
+   (void) joydx;
+   if (!al_is_joystick_installed())
+      return false;
+   if (!al_get_joystick_active(joy))
+      return false;
+   return false;
+   /*
+   if (ljoy->fd <= 0)
+      return false;
+   return whap_fd_can_ff(ljoy->fd);
+   */
+}
+
+
+static bool whap_is_display_haptic(ALLEGRO_DISPLAY *dev)
+{
+   (void)dev;
+   return false;
+}
+
+
+static bool whap_is_keyboard_haptic(ALLEGRO_KEYBOARD *dev)
+{
+   (void)dev;
+   return false;
+}
+
+
+static bool whap_is_touch_input_haptic(ALLEGRO_TOUCH_INPUT *dev)
+{
+   (void)dev;
+   return false;
+}
+
+
+static ALLEGRO_HAPTIC *whap_get_from_mouse(ALLEGRO_MOUSE *mouse)
+{
+   (void)mouse;
+   return NULL;
+}
+
+
+/* Callback to check which effect types are supported. */
+static BOOL CALLBACK
+whap_check_effect_callback(LPCDIEFFECTINFO info, LPVOID data)
+{
+   ALLEGRO_HAPTIC * haptic = (ALLEGRO_HAPTIC *) data;
+   ALLEGRO_HAPTIC_WINDOWS * whap = whap_from_al(haptic);
+   
+   const CAP_MAP * map;
+   for (map = cap_map; map->allegro_bit != -1; map++) {
+      if(GUID_EQUAL(info->guid, map->guid)) {
+         whap->flags |= map->allegro_bit;
+      }
+   }
+   /* Check for more supported effect types. */
+   return DIENUM_CONTINUE;
+}
+
+
+/* Callback to check which axes are supported. */
+static BOOL CALLBACK
+whap_check_axes_calback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID data)
+{
+   ALLEGRO_HAPTIC * haptic = (ALLEGRO_HAPTIC *) data;
+   ALLEGRO_HAPTIC_WINDOWS * whap = whap_from_al(haptic);
+   
+   whap->naxes = 0;
+   if ((dev->dwType & DIDFT_AXIS) && (dev->dwFlags & DIDOI_FFACTUATOR)) {
+
+        whap->axes[whap->naxes] = dev->dwOfs;
+        whap->naxes++;
+        
+        /* Stop if the axes limit is reached */
+        if (whap->naxes >= HAPTICS_AXES_MAX) {
+            return DIENUM_STOP;
+        }
+    }
+
+    return DIENUM_CONTINUE;
+}
+
+
+/* Initializes the haptic device for use with DirectInput */
+static bool whap_initialize_dinput(ALLEGRO_HAPTIC_WINDOWS * whap) {
+   HRESULT ret;
+   ALLEGRO_HAPTIC * haptic = &whap->parent;
+   DIPROPDWORD dipdw;
+   
+   DIDEVCAPS dicaps;
+   /* Get capabilities. */
+   memset((void *) &dicaps, 0, sizeof(dicaps));
+   dicaps.dwSize = sizeof (dicaps);
+   ret = IDirectInputDevice8_GetCapabilities(whap->device, &dicaps);
+   if (FAILED(ret)) {
+      ALLEGRO_WARN("IDirectInputDevice8_GetCapabilities failed on %p\n", whap->device);
+      return false;
+   }
+
+   /** Is it a haptic device? */
+   if ((dicaps.dwFlags & DIDC_FORCEFEEDBACK) != DIDC_FORCEFEEDBACK) {
+      return false; 
+   }
+
+   
+  
+   /* Get number of axes. */
+   ret = IDirectInputDevice8_EnumObjects(whap->device,
+                                          whap_check_axes_calback,
+                                          haptic, DIDFT_AXIS);
+   if (FAILED(ret)) {
+      ALLEGRO_WARN("Could not get haptic device axes ");
+      return false;
+   }
+
+    /* Not needed to acquire the device since the JS driver did that. */
+
+    /* Reset all actuators in case some where active */
+    ret = IDirectInputDevice8_SendForceFeedbackCommand(whap->device,
+                                                       DISFFC_RESET);
+    if (FAILED(ret)) {
+        ALLEGRO_WARN("Could not reset haptic device");
+    }
+
+    /* Enable all actuators. */
+    ret = IDirectInputDevice8_SendForceFeedbackCommand(whap->device,
+                                                       DISFFC_SETACTUATORSON);
+    if (FAILED(ret)) {
+      ALLEGRO_WARN("Could not enable haptic device actuators ");
+      return false;
+    }
+
+    /* Get known supported effects. */
+    ret = IDirectInputDevice8_EnumEffects(whap->device,
+                                          whap_check_effect_callback, haptic,
+                                          DIEFT_ALL);
+     if (FAILED(ret)) {
+      ALLEGRO_WARN("Could not get haptic device supported effects ");
+      return false;
+     }  
+    
+    /** Check if any periodic effects are supported. */
+    bool periodic_ok = al_is_haptic_capable(haptic, ALLEGRO_HAPTIC_SINE);
+    periodic_ok |= al_is_haptic_capable(haptic, ALLEGRO_HAPTIC_SQUARE);
+    periodic_ok |= al_is_haptic_capable(haptic, ALLEGRO_HAPTIC_TRIANGLE);
+    periodic_ok |= al_is_haptic_capable(haptic, ALLEGRO_HAPTIC_SAW_DOWN);
+    periodic_ok |= al_is_haptic_capable(haptic, ALLEGRO_HAPTIC_SAW_UP);
+ 
+    if (periodic_ok) { 
+      /* If we have any of the effects above, we can use 
+         periodic and rumble effects. */
+      whap->flags |= (ALLEGRO_HAPTIC_PERIODIC | ALLEGRO_HAPTIC_RUMBLE); 
+    }  
+   
+    /* Check gain capability and set it to maximum in one go. */
+    dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
+    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
+    dipdw.diph.dwObj        = 0;
+    dipdw.diph.dwHow        = DIPH_DEVICE;
+    dipdw.dwData            = 10000;
+    ret = IDirectInputDevice8_SetProperty(whap->device,
+                                          DIPROP_FFGAIN, &dipdw.diph);
+    if (!FAILED(ret)) {         /* Gain is supported. */
+        whap->flags |= ALLEGRO_HAPTIC_GAIN;
+    }
+    
+    /* Check autocenter and turn it off in one go. */
+    dipdw.diph.dwObj = 0;
+    dipdw.diph.dwHow = DIPH_DEVICE;
+    dipdw.dwData = DIPROPAUTOCENTER_OFF;
+    ret = IDirectInputDevice8_SetProperty(whap->device,
+                                          DIPROP_AUTOCENTER, &dipdw.diph);
+    if (!FAILED(ret)) {         /* Autocenter is supported. */
+        whap->flags |= ALLEGRO_HAPTIC_AUTOCENTER;
+    }
+    return true;
+}
+
+
+
+
+static ALLEGRO_HAPTIC *whap_get_from_joystick(ALLEGRO_JOYSTICK *joy)
+{
+   ALLEGRO_JOYSTICK_DIRECTX * joydx = (ALLEGRO_JOYSTICK_DIRECTX *) joy;
+   ALLEGRO_HAPTIC_WINDOWS * whap;
+   ALLEGRO_HAPTIC * result = NULL;
+   
+   int i;
+
+   if (!al_is_joystick_haptic(joy))
+      return NULL;
+
+   al_lock_mutex(haptic_mutex);
+
+   whap = whap_get_available_haptic();
+
+   if (!whap) {
+      al_unlock_mutex(haptic_mutex);
+      return NULL;
+   }
+
+   whap->parent.device = joy;
+   whap->parent.from   = _AL_HAPTIC_FROM_JOYSTICK;
+
+   whap->guid   = joydx->guid;
+   whap->device = joydx->device;
+   whap->active = true;
+   for (i = 0; i < HAPTICS_EFFECTS_MAX; i++) {
+      whap->effects[i].active = false; /* not in use */
+   }
+   whap->parent.gain = 1.0;
+   whap->parent.autocenter = 0.0;
+   
+   /* result is ok if init functions returns true. */
+   if (whap_initialize_dinput(whap)) {
+      result = &whap->parent;  
+   }
+
+   al_unlock_mutex(haptic_mutex);
+
+   return result;
+}
+
+
+static ALLEGRO_HAPTIC *whap_get_from_display(ALLEGRO_DISPLAY *dev)
+{
+   (void)dev;
+   return NULL;
+}
+
+
+static ALLEGRO_HAPTIC *whap_get_from_keyboard(ALLEGRO_KEYBOARD *dev)
+{
+   (void)dev;
+   return NULL;
+}
+
+
+static ALLEGRO_HAPTIC *whap_get_from_touch_input(ALLEGRO_TOUCH_INPUT *dev)
+{
+   (void)dev;
+   return NULL;
+}
+
+
+static int whap_get_capabilities(ALLEGRO_HAPTIC *dev)
+{
+   ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(dev);
+   return whap->flags;
+}
+
+
+static double whap_get_gain(ALLEGRO_HAPTIC *dev)
+{
+   ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(dev);
+   /* Unfortunately there seems to be no API to GET gain, only to set?!
+    * So, return the stored gain.
+    */
+   return whap->parent.gain;
+}
+
+
+static bool whap_set_gain(ALLEGRO_HAPTIC *dev, double gain)
+{
+   ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(dev);
+   (void) whap, (void) gain;
+   return false;
+}
+
+
+double whap_get_autocenter (ALLEGRO_HAPTIC * dev) 
+{
+   ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(dev);
+   /* Unfortunately there seems to be no API to GET gain, only to set?!
+    * So, return the stored gain.
+    */
+   return whap->parent.gain;
+}
+
+
+static bool whap_set_autocenter(ALLEGRO_HAPTIC *dev, double intensity)
+{
+   ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(dev);
+   (void) whap, (void) intensity;
+   return false;
+}
+
+static int whap_get_num_effects(ALLEGRO_HAPTIC *dev)
+{
+   ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(dev);
+   int n_effects;
+   (void) n_effects, (void)whap;
+   
+   return HAPTICS_EFFECTS_MAX;
+}
+
+
+static bool whap_is_effect_ok(ALLEGRO_HAPTIC *haptic,
+                              ALLEGRO_HAPTIC_EFFECT *effect)
+{
+   int caps;
+
+   caps = al_get_haptic_capabilities(haptic);
+   if (caps & effect->type) {
+      return true; // whap_effect2lin(&leff, effect);
+   }
+   return false;
+}
+
+
+static double whap_effect_duration(ALLEGRO_HAPTIC_EFFECT *effect)
+{
+   return effect->replay.delay + effect->replay.length;
+}
+
+
+static bool whap_upload_effect(ALLEGRO_HAPTIC *dev,
+   ALLEGRO_HAPTIC_EFFECT *effect, ALLEGRO_HAPTIC_EFFECT_ID *id)
+{
+   ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(dev);
+
+   ASSERT(dev);
+   ASSERT(id);
+   ASSERT(effect);
+
+   /* Set id's values to indicate failure. */
+   id->_haptic = NULL;
+   id->_id     = -1;
+   id->_handle = -1;
+   return false;
+}
+
+
+static bool whap_play_effect(ALLEGRO_HAPTIC_EFFECT_ID *id, int loops)
+{
+   ALLEGRO_HAPTIC_WINDOWS *whap = (ALLEGRO_HAPTIC_WINDOWS *) id->_haptic;
+   /*
+   struct input_event play;
+   int fd;
+   double now;
+   double duration;
+   */
+
+   if (!whap)
+      return false;
+
+   return false;
+}
+
+
+static bool whap_stop_effect(ALLEGRO_HAPTIC_EFFECT_ID *id)
+{
+   ALLEGRO_HAPTIC_WINDOWS *whap = (ALLEGRO_HAPTIC_WINDOWS *) id->_haptic;
+ 
+   if (!whap)
+      return false;
+   return false;
+}
+
+
+static bool whap_is_effect_playing(ALLEGRO_HAPTIC_EFFECT_ID *id)
+{
+   ASSERT(id);
+
+   /* 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.
+    */
+   return (id->_playing && al_get_time() < id->_end_time);
+}
+
+
+static bool whap_release_effect(ALLEGRO_HAPTIC_EFFECT_ID *id)
+{
+   ALLEGRO_HAPTIC_WINDOWS *whap = (ALLEGRO_HAPTIC_WINDOWS *)id->_haptic;
+
+   whap_stop_effect(id);
+   whap->effects[id->_id].active = false; /* not in use */
+   return true;
+}
+
+
+static bool whap_release(ALLEGRO_HAPTIC *haptic)
+{
+   ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(haptic);
+   ASSERT(haptic);
+
+   if (!whap->active)
+      return false;
+
+   whap->active = false;
+   whap->device = NULL;
+   return true;
+}
+
+
+
+/* vim: set sts=3 sw=3 et: */
-- 
1.7.10.4


From 99f685a29446e3163c4754df4d01e8cdb94c60f4 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Tue, 29 Apr 2014 07:25:02 +0200
Subject: [PATCH 04/15] Haptic Driver for windows. Should work in theory.

---
 docs/src/refman/haptic.txt |    8 +-
 src/win/whaptic.cpp        |  759 ++++++++++++++++++++++++++++++--------------
 2 files changed, 520 insertions(+), 247 deletions(-)

diff --git a/docs/src/refman/haptic.txt b/docs/src/refman/haptic.txt
index 75a164b..59a1f4f 100644
--- a/docs/src/refman/haptic.txt
+++ b/docs/src/refman/haptic.txt
@@ -401,7 +401,9 @@ a haptic effect. The intensity parameter should be passed with a value
 between 0.0 and 1.0. The value 0.0 means automatic centering is 
 disabled, and 1.0 means full strength automatic centering. Any value
 in between those two extremes will result in partial automatic 
-centering.
+centering. Some platforms do not support partial automatic 
+centering. If that is the case, a value of less than 0.5 will turn 
+it off, while a value equal to or higher to 0.5 will turn it on.
 Returns true if set sucessfully, false if not.
 Can only work if [al_get_haptic_capabilities] returns a value that has
 [ALLEGRO_HAPTIC_AUTOCENTER] set.  If not, this function returns false.
@@ -417,7 +419,9 @@ a haptic effect. The return value can be between 0.0 and 1.0.
 The value 0.0 means automatic centering is disabled, and 1.0 means 
 automatic centering is enabled at full strength. Any value
 in between those two extremes means partial automatic 
-centering is enabled.
+centering is enabled. Some platforms do not support partial automatic 
+centering. If that is the case, a value of less than 0.5 means it is turned 
+off, while a value equal to or higher to 0.5 means it is turned on.
 Can only work if [al_get_haptic_capabilities] returns a 
 value that has [ALLEGRO_HAPTIC_AUTOCENTER] set. If not, this function 
 returns 0.0.
diff --git a/src/win/whaptic.cpp b/src/win/whaptic.cpp
index 2ff7547..fadf83c 100644
--- a/src/win/whaptic.cpp
+++ b/src/win/whaptic.cpp
@@ -61,15 +61,31 @@ ALLEGRO_DEBUG_CHANNEL("whaptic")
 /* Support at most 3 axes per device. */
 #define HAPTICS_AXES_MAX        3
 
+/** This union is needed to avoid 
+ dynamical memory allocation. */
+
+typedef union {
+  DICONSTANTFORCE constant;
+  DIRAMPFORCE     ramp;
+  DIPERIODIC      periodic;
+  DICONDITION     condition;
+  DICUSTOMFORCE   custom;
+} ALLEGRO_HAPTIC_PARAMETER_WINDOWS;
+
+
 /*
- * Haptic system effect data.
+ * Haptic effect system data.
  */
 typedef struct 
 {
-   bool                active;
-   DIEFFECT            effect;
-   LPDIRECTINPUTEFFECT ref;
-   /* XINPUT_VIBRATION vibration; */
+   bool                                 active;
+   DIEFFECT                             effect;
+   DIENVELOPE                           envelope;
+   LPDIRECTINPUTEFFECT                  ref;
+   DWORD                                axes[HAPTICS_AXES_MAX];
+   LONG                                 directions[HAPTICS_AXES_MAX];
+   ALLEGRO_HAPTIC_PARAMETER_WINDOWS     parameter;
+   const GUID                         * guid;
 } ALLEGRO_HAPTIC_EFFECT_WINDOWS ;
 
 
@@ -241,14 +257,56 @@ static ALLEGRO_HAPTIC_WINDOWS *whap_get_available_haptic(void)
    return NULL;
 }
 
+ /* Look for a free haptic effect slot for a device and return it, 
+  * or NULL if exhausted. Also initializes the effect 
+  * reference to NULL. */
+static ALLEGRO_HAPTIC_EFFECT_WINDOWS *whap_get_available_effect(
+  ALLEGRO_HAPTIC_WINDOWS * whap) {
+  ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff; 
+  int i;
+   for (i = 0; i < al_get_num_haptic_effects(&whap->parent); i++) {
+      if (!whap->effects[i].active) {
+         weff = whap->effects + i;
+         weff->active = true;
+         weff->ref    = NULL;
+         return weff;
+      }
+   }
+   return NULL;
+}
+
+
+/* Releases a windows haptics effect and unloads it from the device. */
+static bool whap_release_effect_windows(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff) {
+   bool result = true;
+   if(!weff->active) return false; /* already not in use, bail out. */
+    
+   /* Unload the effect from the device. */
+   if(weff->ref) { 
+      HRESULT ret;     
+      ret = IDirectInputEffect_Unload(weff->ref);
+      if (FAILED(ret)) { 
+         ALLEGRO_WARN("Could not unload effect.");
+         result = false;
+      }
+   }
+   /* Custom force needs to clean up it's data. */
+   if (weff->guid == &GUID_CustomForce) { 
+    al_free(weff->parameter.custom.rglForceData);
+    weff->parameter.custom.rglForceData = NULL;
+   }   
+   weff->active = false; /* not in use */
+   weff->ref    = NULL; /* No reference to effect anymore. */  
+   return result;
+}
+
 
 /* Converts a generic haptic device to a Windows-specific one. */
 static ALLEGRO_HAPTIC_WINDOWS *whap_from_al(ALLEGRO_HAPTIC *hap)
 {
-   return (ALLEGRO_HAPTIC_WINDOWS *) hap;
+  return (ALLEGRO_HAPTIC_WINDOWS *) hap;
 }
 
-
 static void whap_exit_haptic(void)
 {
    ASSERT(haptic_mutex);
@@ -256,243 +314,334 @@ static void whap_exit_haptic(void)
    haptic_mutex = NULL;
 }
 
-#ifdef _COMMENT_
-static bool whap_type2win(__u16 *res, int type)
+/* Convert the type of the periodic allegro effect to the windows effect*/
+static bool whap_periodictype2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, ALLEGRO_HAPTIC_EFFECT * effect)
 {
-   ASSERT(res);
+  switch (effect->data.periodic.waveform) {
+    case ALLEGRO_HAPTIC_SINE: 
+      weff->guid = &GUID_Sine;      
+      return true;
+      
+    case ALLEGRO_HAPTIC_SQUARE: 
+      weff->guid = &GUID_Square;
+      return true;
+      
+    case ALLEGRO_HAPTIC_TRIANGLE: 
+      weff->guid = &GUID_Triangle;
+      return true;
+      
+    case ALLEGRO_HAPTIC_SAW_UP: 
+      weff->guid = &GUID_SawtoothUp;
+      return true;
+      
+    case ALLEGRO_HAPTIC_SAW_DOWN: 
+      weff->guid = &GUID_SawtoothDown;
+      return true;
+      
+    case ALLEGRO_HAPTIC_CUSTOM: 
+      weff->guid = &GUID_CustomForce;
+      return true;
+    default:
+      return false;
+  }
+}
 
-   switch (type) {
+/* Convert the type of the allegro effect to the windows effect*/
+static bool whap_type2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, ALLEGRO_HAPTIC_EFFECT * effect)
+{
+   switch (effect->type) {
       case ALLEGRO_HAPTIC_RUMBLE:
-         (*res) = FF_RUMBLE;
-         break;
+         weff->guid = &GUID_Sine;
+         return true;
       case ALLEGRO_HAPTIC_PERIODIC:
-         (*res) = FF_PERIODIC;
-         break;
+         return whap_periodictype2win(weff, effect);
       case ALLEGRO_HAPTIC_CONSTANT:
-         (*res) = FF_CONSTANT;
-         break;
+         weff->guid = &GUID_ConstantForce;
+         return true;
       case ALLEGRO_HAPTIC_SPRING:
-         (*res) = FF_SPRING;
-         break;
+         weff->guid = &GUID_Spring;
+         return true;
       case ALLEGRO_HAPTIC_FRICTION:
-         (*res) = FF_FRICTION;
-         break;
+         weff->guid = &GUID_Friction;
+         return true;
       case ALLEGRO_HAPTIC_DAMPER:
-         (*res) = FF_DAMPER;
-         break;
+         weff->guid = &GUID_Damper;
+         return true;
       case ALLEGRO_HAPTIC_INERTIA:
-         (*res) = FF_INERTIA;
-         break;
+         weff->guid = &GUID_Inertia;
+         return true;
       case ALLEGRO_HAPTIC_RAMP:
-         (*res) = FF_RAMP;
-         break;
+         weff->guid = &GUID_RampForce;
+         return true;
       default:
-         return false;
+         return NULL;
    }
    return true;
 }
 
-
-static bool whap_wave2lin(__u16 *res, int type)
+/* Convert the direction of the allegro effect to the windows effect*/
+static bool whap_direction2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+                               ALLEGRO_HAPTIC_EFFECT * effect,
+                               ALLEGRO_HAPTIC_WINDOWS * whap)
 {
-   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;
-   }
+   int index;
+   /* Use spherical coordinates in correspondance with the Allegro API. */
+   weff->effect.dwFlags          = DIEFF_SPHERICAL | DIEFF_OBJECTOFFSETS;
+   /* Prepare axes. */
+   weff->effect.cAxes            = whap->naxes;
+   memset((void *) weff->axes      , 0, sizeof(weff->axes));   
+   for(index = 0; index < whap->naxes; index ++) {
+      weff->axes[index] = whap->axes[index];
+   }  
+   weff->effect.rgdwAxes         = weff->axes;
+   /* Set up directions as well.. */
+   memset((void *) weff->directions, 0, sizeof(weff->directions));
+   
+   /* Zero or 1 axes means no direction anyway, start concerting from 2 
+    * available axes on the device.  
+    * 
+    * Two axes or more means we have to convert the angle of the 
+    * allegro effect's direction to that which DirectInput uses.
+    * Directinput starts on the right, while Allegro  API towards the 
+    * user, both clockwise. In Allegro API, right is 3*PI/2, so substract 
+    * that first, then scale to degrees * 100. 
+    */  
+   if (whap->naxes > 1) {   
+     double angle = effect->direction.angle - ((3.0* ALLEGRO_PI) / 2.0);
+     weff->directions[0] = (long) (angle * 36000.0 / (2.0*ALLEGRO_PI));
+   } 
+   
+   /* Three axes or more. Convert the azimuth of the allegro effect.
+    * The allegro azimuth is 0 in the plane of the user and increases up.
+    * The rotation in dinput is in the inverse direction (bummer) 
+    * but fortunately it starts also at 0 in the plane of the user as it 
+    * is in the allegro API.     
+    */
+   if (whap->naxes > 2) {
+      double azimuth = - effect->direction.azimuth;      
+      weff->directions[1] = (long) (azimuth * 36000.0 / (ALLEGRO_PI/2.0));
+   }    
+   weff->effect.rglDirection     = weff->directions;  
    return true;
 }
 
-
-/* Converts the time in seconds to a Linux-compatible time.
+/* Converts the time in seconds to a Windows-compatible time.
  * Return false if out of bounds.
  */
-static bool whap_time2lin(__u16 *res, double sec)
+static bool whap_time2win(DWORD *res, double sec)
 {
    ASSERT(res);
 
-   if (sec < 0.0 || sec > 32.767)
+   if (sec < 0.0 || sec >= 4294.967296)
       return false;
-   (*res) = (__u16) round(sec * 1000.0);
+   (*res) = (DWORD) floor(sec * 1000000.0);
    return true;
 }
 
-
-/* Converts the time in seconds to a Linux-compatible time.
- * Return false if out of bounds. This one allows negative times.
+/* Converts the level in range 0.0 to 1.0 to a Windows-compatible level.
+ * Returns false if out of bounds.
  */
-static bool whap_stime2lin(__s16 *res, double sec)
+static bool whap_level2win(DWORD *res, double level)
 {
    ASSERT(res);
 
-   if (sec < -32.767 || sec > 32.767)
+   if (level < 0.0 || level > 1.0)
       return false;
-   (*res) = (__s16) round(sec * 1000.0);
+   *res = (DWORD) floor(level * DI_FFNOMINALMAX);
    return true;
 }
 
 
-/* Converts replay data to Linux-compatible data. */
-static bool whap_replay2lin(struct ff_replay *lin,
-   struct ALLEGRO_HAPTIC_REPLAY *al)
-{
-   return whap_time2lin(&lin->delay, al->delay)
-      && whap_time2lin(&lin->length, al->length);
-}
-
-
-/* Converts the level in range 0.0 to 1.0 to a Linux-compatible level.
+/* Converts the level in range -1.0 to 1.0 to a Windows-compatible level.
  * Returns false if out of bounds.
  */
-static bool whap_level2lin(__u16 *res, double level)
+static bool whap_slevel2win(LONG*res, double level)
 {
    ASSERT(res);
 
-   if (level < 0.0 || level > 1.0)
+   if (level < 1.0 || level > 1.0)
       return false;
-   *res = (__u16) round(level * (double)0x7fff);
+   *res = (LONG) (level * DI_FFNOMINALMAX);
    return true;
 }
 
-
-/* Converts the level in range -1.0 to 1.0 to a Linux-compatible level.
+/* Converts a phase in range 0.0 to 1.0 to a Windows-compatible level.
  * Returns false if out of bounds.
  */
-static bool whap_slevel2lin(__s16 *res, double level)
+static bool whap_phase2win(DWORD * res, double phase)
 {
    ASSERT(res);
 
-   if (level < -1.0 || level > 1.0)
+   if (phase < 0.0 || phase > 1.0)
       return false;
-   *res = (__s16) round(level * (double)0x7ffe);
+   *res = (DWORD) (phase * 35999);
    return true;
 }
 
 
-/* Converts an Allegro haptic effect envelope to Linux input API. */
-static bool whap_envelope2lin(struct ff_envelope *lin,
-   struct ALLEGRO_HAPTIC_ENVELOPE *al)
+/* Converts replay data to Widows-compatible data. */
+static bool whap_replay2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+                               ALLEGRO_HAPTIC_EFFECT * effect)
 {
-   return whap_time2lin(&lin->attack_length, al->attack_length)
-      && whap_time2lin(&lin->fade_length, al->fade_length)
-      && whap_level2lin(&lin->attack_level, al->attack_level)
-      && whap_level2lin(&lin->fade_level, al->fade_level);
+   return whap_time2win(&weff->effect.dwStartDelay, effect->replay.delay)
+      && whap_time2win(&weff->effect.dwDuration,  effect->replay.length);
 }
 
 
-/* Converts a rumble effect to Linux input API. */
-static bool whap_rumble2lin(struct ff_rumble_effect *lin,
-   struct ALLEGRO_HAPTIC_RUMBLE_EFFECT *al)
+/* Converts an Allegro haptic effect envelope to directinput API. */
+static bool whap_envelope2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+                               ALLEGRO_HAPTIC_ENVELOPE * aenv)
 {
-   return whap_level2lin(&lin->strong_magnitude, al->strong_magnitude)
-      && whap_level2lin(&lin->weak_magnitude, al->weak_magnitude);
+  /* Prepare envelope. */
+   DIENVELOPE              * wenv = &weff->envelope;
+   memset((void *) wenv, 0, sizeof(DIENVELOPE));
+   weff->envelope.dwSize         = sizeof(DIENVELOPE);  
+   weff->effect.lpEnvelope       = wenv;
+  
+   return whap_time2win(&wenv->dwAttackTime     , aenv->attack_length)
+      && whap_time2win(&wenv->dwFadeTime        , aenv->fade_length)
+      && whap_level2win(&wenv->dwAttackLevel    , aenv->attack_level)
+      && whap_level2win(&wenv->dwFadeLevel      , aenv->fade_level);
 }
 
-
-/* Converts a constant effect to Linux input API. */
-static bool whap_constant2lin(struct ff_constant_effect *lin,
-   struct ALLEGRO_HAPTIC_CONSTANT_EFFECT *al)
-{
-   return whap_envelope2lin(&lin->envelope, &al->envelope)
-      && whap_slevel2lin(&lin->level, al->level);
+/* Converts a constant effect to directinput API. */
+static bool whap_constant2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+                               ALLEGRO_HAPTIC_EFFECT * effect) 
+{ 
+   weff->effect.cbTypeSpecificParams  = sizeof(weff->parameter.constant);
+   weff->effect.lpvTypeSpecificParams = &weff->parameter.constant;
+   return whap_envelope2win(weff, &effect->data.constant.envelope)
+      && whap_slevel2win(&weff->parameter.constant.lMagnitude, effect->data.constant.level);
 }
 
 
-/* Converts a ramp effect to Linux input API. */
-static bool whap_ramp2lin(struct ff_ramp_effect *lin,
-   struct ALLEGRO_HAPTIC_RAMP_EFFECT *al)
+/* Converts a ramp effect to directinput input API. */
+static bool whap_ramp2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+                               ALLEGRO_HAPTIC_EFFECT * effect)
 {
-   return whap_envelope2lin(&lin->envelope, &al->envelope)
-      && whap_slevel2lin(&lin->start_level, al->start_level)
-      && whap_slevel2lin(&lin->end_level, al->end_level);
+  weff->effect.cbTypeSpecificParams  = sizeof(weff->parameter.ramp);
+  weff->effect.lpvTypeSpecificParams = &weff->parameter.ramp;
+      
+  return whap_envelope2win(weff, &effect->data.ramp.envelope)
+      && whap_slevel2win(&weff->parameter.ramp.lStart, effect->data.ramp.start_level)
+      && whap_slevel2win(&weff->parameter.ramp.lEnd  , effect->data.ramp.end_level);
 }
 
-
-/* Converts a ramp effect to Linux input API. */
-static bool whap_condition2lin(struct ff_condition_effect *lin,
-   struct ALLEGRO_HAPTIC_CONDITION_EFFECT *al)
+/* Converts a condition effect to directinput input API. */
+static bool whap_condition2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+                               ALLEGRO_HAPTIC_EFFECT * effect)
 {
-   return whap_slevel2lin(&lin->center, al->center)
-      && whap_level2lin(&lin->deadband, al->deadband)
-      && whap_slevel2lin(&lin->right_coeff, al->right_coeff)
-      && whap_level2lin(&lin->right_saturation, al->right_saturation)
-      && whap_slevel2lin(&lin->left_coeff, al->left_coeff)
-      && whap_level2lin(&lin->left_saturation, al->left_saturation);
+  weff->effect.cbTypeSpecificParams  = sizeof(weff->parameter.condition);
+  weff->effect.lpvTypeSpecificParams = &weff->parameter.condition;
+  /* XXX: no envelope here ???  */
+      
+  return whap_level2win(&weff->parameter.condition.dwNegativeSaturation, effect->data.condition.left_saturation)
+      && whap_level2win(&weff->parameter.condition.dwPositiveSaturation, effect->data.condition.right_saturation)      
+      && whap_slevel2win(&weff->parameter.condition.lNegativeCoefficient, effect->data.condition.left_coeff)
+      && whap_slevel2win(&weff->parameter.condition.lPositiveCoefficient, effect->data.condition.right_coeff)
+      && whap_slevel2win(&weff->parameter.condition.lDeadBand          , effect->data.condition.deadband)
+      && whap_slevel2win(&weff->parameter.condition.lOffset            , effect->data.condition.center);
 }
 
+/* Converts a custom effect to directinput input API. */
+static bool whap_custom2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+                            ALLEGRO_HAPTIC_EFFECT * effect) {
+  int index;
+  weff->effect.cbTypeSpecificParams  = sizeof(weff->parameter.custom);
+  weff->effect.lpvTypeSpecificParams = &weff->parameter.custom;
+  weff->parameter.custom.cChannels = 1;
+  weff->parameter.custom.cSamples  = effect->data.periodic.custom_len;
+  /* Use al malloc only in this case since the custom_data can be arbitrarily long. */
+  weff->parameter.custom.rglForceData = (LONG *) al_malloc(sizeof(LONG) * effect->data.periodic.custom_len);
+  if(!weff->parameter.custom.rglForceData) return false;
+  /* Gotta copy this to long values, and scale them too... */
+  for(index =0; index < effect->data.periodic.custom_len; index ++) {
+    weff->parameter.custom.rglForceData[index] =
+    (LONG)(effect->data.periodic.custom_data[index] * ((double)(1<<31)));    
+  }
+  return true;
+}
 
-/* Converts a periodic effect to linux input API. */
-static bool whap_periodic2lin(struct ff_periodic_effect *lin,
-   struct ALLEGRO_HAPTIC_PERIODIC_EFFECT *al)
-{
-   /* Custom data is not supported yet, because currently no Linux
-    * haptic driver supports it.
-    */
-   if (al->custom_data)
-      return false;
 
-   return whap_slevel2lin(&lin->magnitude, al->magnitude)
-      && whap_stime2lin(&lin->offset, al->offset)
-      && whap_time2lin(&lin->period, al->period)
-      && whap_time2lin(&lin->phase, al->phase)
-      && whap_wave2lin(&lin->waveform, al->waveform)
-      && whap_envelope2lin(&lin->envelope, &al->envelope);
+/* Converts a periodic effect to directinput input API. */
+static bool whap_periodic2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+                               ALLEGRO_HAPTIC_EFFECT * effect)
+{  
+  if (effect->data.periodic.waveform == ALLEGRO_HAPTIC_CUSTOM) {
+    return whap_custom2win(weff, effect);
+  }
+  
+  weff->effect.cbTypeSpecificParams  = sizeof(weff->parameter.periodic);
+  weff->effect.lpvTypeSpecificParams = &weff->parameter.periodic;
+      
+  return whap_envelope2win(weff, &effect->data.periodic.envelope)
+      && whap_level2win(&weff->parameter.periodic.dwMagnitude, effect->data.periodic.magnitude)
+      && whap_phase2win(&weff->parameter.periodic.dwPhase    , effect->data.periodic.phase)    
+      && whap_time2win(&weff->parameter.periodic.dwPeriod    , effect->data.periodic.period)    
+      && whap_slevel2win(&weff->parameter.periodic.lOffset   , effect->data.periodic.offset);
 }
 
 
-/* Converts Allegro haptic effect to Linux input API. */
-static bool whap_effect2lin(struct ff_effect *lin, ALLEGRO_HAPTIC_EFFECT *al)
+/* Converts a periodic effect to directinput input API. */
+static bool whap_rumble2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+                               ALLEGRO_HAPTIC_EFFECT * effect)
+{  
+  weff->effect.cbTypeSpecificParams  = sizeof(weff->parameter.periodic);
+  weff->effect.lpvTypeSpecificParams = &weff->parameter.periodic;
+  /** Set up a simple sine wave effect for a rumble. */
+  weff->guid                        = &GUID_Sine;        
+  return whap_level2win(&weff->parameter.periodic.dwMagnitude, effect->data.rumble.strong_magnitude)
+      && whap_phase2win(&weff->parameter.periodic.dwPhase    , 0)    
+      && whap_time2win(&weff->parameter.periodic.dwPeriod    , 250000)    
+      && whap_slevel2win(&weff->parameter.periodic.lOffset   , 0);
+}
+
+/* Converts Allegro haptic effect to dinput API. */
+static bool whap_effect2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+                            ALLEGRO_HAPTIC_EFFECT *effect, 
+                            ALLEGRO_HAPTIC_WINDOWS *whap)
 {
-   memset(lin, 0, sizeof(*lin));
+   /* Generic setup */ 
+   memset((void *) weff, 0, sizeof(*weff));
+   /* Set global stuff. */
+   weff->effect.dwSize           = sizeof(DIEFFECT);
+   weff->effect.dwGain           = DI_FFNOMINALMAX;       
+   weff->effect.dwSamplePeriod   = 0;
+   weff->effect.dwFlags          = DIEFF_OBJECTOFFSETS;
+   weff->effect.lpEnvelope       = NULL;
+  
+   if (!whap_type2win(weff, effect)) {
+      return false;
+   }
 
-   if (!whap_type2lin(&lin->type, al->type))
+   if (!whap_direction2win(weff, effect, whap)) {
       return false;
-   /* lin_effect->replay = effect->re; */
-   lin->direction = (__u16)
-      round(((double)0xC000 * al->direction.angle) / (2 * M_PI));
-   lin->id = -1;
-   if (!whap_replay2lin(&lin->replay, &al->replay))
+   }
+   
+   if (!whap_replay2win(weff, effect)) {
       return false;
-   switch (lin->type) {
-      case FF_RUMBLE:
-         return whap_rumble2lin(&lin->u.rumble, &al->data.rumble);
-      case FF_PERIODIC:
-         return whap_periodic2lin(&lin->u.periodic, &al->data.periodic);
-      case FF_CONSTANT:
-         return whap_constant2lin(&lin->u.constant, &al->data.constant);
-      case FF_RAMP:
-         return whap_ramp2lin(&lin->u.ramp, &al->data.ramp);
-      case FF_SPRING:   /* fall through */
-      case FF_FRICTION: /* fall through */
-      case FF_DAMPER:   /* fall through */
-      case FF_INERTIA:
-         return whap_condition2lin(&lin->u.condition[0], &al->data.condition);
-      default:
-         return false;
+   }
+   
+   
+   switch (effect->type) {
+     case ALLEGRO_HAPTIC_RUMBLE:
+       return whap_rumble2win(weff, effect);
+     case ALLEGRO_HAPTIC_PERIODIC:
+       return whap_periodic2win(weff, effect);
+     case ALLEGRO_HAPTIC_CONSTANT:
+       return whap_constant2win(weff, effect);
+     case ALLEGRO_HAPTIC_RAMP:
+       return whap_ramp2win(weff, effect);
+     case ALLEGRO_HAPTIC_SPRING:
+     case ALLEGRO_HAPTIC_FRICTION:
+     case ALLEGRO_HAPTIC_DAMPER:
+     case ALLEGRO_HAPTIC_INERTIA:
+       return whap_condition2win(weff, effect);
+     default: 
+       return false;
    }
 }
 
-#endif // __COMMENT__
-
 static bool whap_get_active(ALLEGRO_HAPTIC *haptic)
 {
    ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(haptic);
@@ -500,6 +649,24 @@ static bool whap_get_active(ALLEGRO_HAPTIC *haptic)
 }
 
 
+static bool whap_is_dinput_device_haptic(LPDIRECTINPUTDEVICE2 device) {
+   HRESULT ret;
+   DIDEVCAPS dicaps;
+   /* Get capabilities. */
+   memset((void *) &dicaps, 0, sizeof(dicaps));
+   dicaps.dwSize = sizeof (dicaps);
+   ret = IDirectInputDevice8_GetCapabilities(device, &dicaps);
+   if (FAILED(ret)) {
+      ALLEGRO_WARN("IDirectInputDevice8_GetCapabilities failed on %p\n", device);
+      return false;
+   }
+   
+   /** Is it a haptic device? */
+   return ((dicaps.dwFlags & DIDC_FORCEFEEDBACK) == DIDC_FORCEFEEDBACK);
+}
+
+
+
 static bool whap_is_mouse_haptic(ALLEGRO_MOUSE *mouse)
 {
    (void)mouse;
@@ -515,12 +682,7 @@ static bool whap_is_joystick_haptic(ALLEGRO_JOYSTICK *joy)
       return false;
    if (!al_get_joystick_active(joy))
       return false;
-   return false;
-   /*
-   if (ljoy->fd <= 0)
-      return false;
-   return whap_fd_can_ff(ljoy->fd);
-   */
+   return whap_is_dinput_device_haptic(joydx->device);
 }
 
 
@@ -552,6 +714,51 @@ static ALLEGRO_HAPTIC *whap_get_from_mouse(ALLEGRO_MOUSE *mouse)
 }
 
 
+/* Sets the force feedback gain on a directinput device.
+ Returns true on success and false on failure.  */
+static bool whap_set_dinput_device_gain(LPDIRECTINPUTDEVICE2 device, 
+                                        double gain) {
+   HRESULT ret;
+   DIPROPDWORD dipdw;
+   if (gain < 0.0) return false;
+   if (gain > 1.0) return false;
+   
+   dipdw.diph.dwSize = sizeof(DIPROPDWORD);
+   dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
+   dipdw.diph.dwObj = 0;
+   dipdw.diph.dwHow = DIPH_DEVICE;
+   dipdw.dwData     = gain * DI_FFNOMINALMAX; 
+   
+   ret = IDirectInputDevice8_SetProperty(device,
+                                          DIPROP_FFGAIN, &dipdw.diph);
+   return (!FAILED(ret));
+}
+
+/* Sets the force feedback autocentering intensity on a directinput device.
+ Returns true on success and false on failure. */
+static bool whap_set_dinput_device_autocenter(LPDIRECTINPUTDEVICE2 device, 
+                                              double intensity) {
+   HRESULT ret;
+   DIPROPDWORD dipdw;
+   if (intensity < 0.0) return false;
+   if (intensity > 1.0) return false;
+   
+   dipdw.diph.dwSize = sizeof(DIPROPDWORD);
+   dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
+   dipdw.diph.dwObj = 0;
+   dipdw.diph.dwHow = DIPH_DEVICE;
+   if (intensity < 0.5) { 
+      dipdw.dwData = DIPROPAUTOCENTER_OFF;
+   } else {     
+      dipdw.dwData = DIPROPAUTOCENTER_ON;
+   }
+   /* Try to set the autocenter. */
+   ret = IDirectInputDevice8_SetProperty(device,
+                                         DIPROP_AUTOCENTER, &dipdw.diph);
+   return (!FAILED(ret));
+}
+
+
 /* Callback to check which effect types are supported. */
 static BOOL CALLBACK
 whap_check_effect_callback(LPCDIEFFECTINFO info, LPVOID data)
@@ -597,25 +804,7 @@ whap_check_axes_calback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID data)
 static bool whap_initialize_dinput(ALLEGRO_HAPTIC_WINDOWS * whap) {
    HRESULT ret;
    ALLEGRO_HAPTIC * haptic = &whap->parent;
-   DIPROPDWORD dipdw;
    
-   DIDEVCAPS dicaps;
-   /* Get capabilities. */
-   memset((void *) &dicaps, 0, sizeof(dicaps));
-   dicaps.dwSize = sizeof (dicaps);
-   ret = IDirectInputDevice8_GetCapabilities(whap->device, &dicaps);
-   if (FAILED(ret)) {
-      ALLEGRO_WARN("IDirectInputDevice8_GetCapabilities failed on %p\n", whap->device);
-      return false;
-   }
-
-   /** Is it a haptic device? */
-   if ((dicaps.dwFlags & DIDC_FORCEFEEDBACK) != DIDC_FORCEFEEDBACK) {
-      return false; 
-   }
-
-   
-  
    /* Get number of axes. */
    ret = IDirectInputDevice8_EnumObjects(whap->device,
                                           whap_check_axes_calback,
@@ -664,27 +853,15 @@ static bool whap_initialize_dinput(ALLEGRO_HAPTIC_WINDOWS * whap) {
       whap->flags |= (ALLEGRO_HAPTIC_PERIODIC | ALLEGRO_HAPTIC_RUMBLE); 
     }  
    
-    /* Check gain capability and set it to maximum in one go. */
-    dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
-    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
-    dipdw.diph.dwObj        = 0;
-    dipdw.diph.dwHow        = DIPH_DEVICE;
-    dipdw.dwData            = 10000;
-    ret = IDirectInputDevice8_SetProperty(whap->device,
-                                          DIPROP_FFGAIN, &dipdw.diph);
-    if (!FAILED(ret)) {         /* Gain is supported. */
-        whap->flags |= ALLEGRO_HAPTIC_GAIN;
+    if (whap_set_dinput_device_gain(whap->device, 1.0)) { 
+      whap->flags |= ALLEGRO_HAPTIC_GAIN;
     }
     
     /* Check autocenter and turn it off in one go. */
-    dipdw.diph.dwObj = 0;
-    dipdw.diph.dwHow = DIPH_DEVICE;
-    dipdw.dwData = DIPROPAUTOCENTER_OFF;
-    ret = IDirectInputDevice8_SetProperty(whap->device,
-                                          DIPROP_AUTOCENTER, &dipdw.diph);
-    if (!FAILED(ret)) {         /* Autocenter is supported. */
-        whap->flags |= ALLEGRO_HAPTIC_AUTOCENTER;
+    if (whap_set_dinput_device_autocenter(whap->device, 0.0)) { 
+      whap->flags |= ALLEGRO_HAPTIC_AUTOCENTER;
     }
+ 
     return true;
 }
 
@@ -695,7 +872,6 @@ static ALLEGRO_HAPTIC *whap_get_from_joystick(ALLEGRO_JOYSTICK *joy)
 {
    ALLEGRO_JOYSTICK_DIRECTX * joydx = (ALLEGRO_JOYSTICK_DIRECTX *) joy;
    ALLEGRO_HAPTIC_WINDOWS * whap;
-   ALLEGRO_HAPTIC * result = NULL;
    
    int i;
 
@@ -724,13 +900,15 @@ static ALLEGRO_HAPTIC *whap_get_from_joystick(ALLEGRO_JOYSTICK *joy)
    whap->parent.autocenter = 0.0;
    
    /* result is ok if init functions returns true. */
-   if (whap_initialize_dinput(whap)) {
-      result = &whap->parent;  
+   if (!whap_initialize_dinput(whap)) {
+      al_release_haptic(&whap->parent); 
+      al_unlock_mutex(haptic_mutex);
+      return NULL;  
    }
 
    al_unlock_mutex(haptic_mutex);
-
-   return result;
+   
+   return &whap->parent;
 }
 
 
@@ -765,9 +943,7 @@ static int whap_get_capabilities(ALLEGRO_HAPTIC *dev)
 static double whap_get_gain(ALLEGRO_HAPTIC *dev)
 {
    ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(dev);
-   /* Unfortunately there seems to be no API to GET gain, only to set?!
-    * So, return the stored gain.
-    */
+   /* Just return the stored gain, it's easier than querying. */
    return whap->parent.gain;
 }
 
@@ -775,26 +951,34 @@ static double whap_get_gain(ALLEGRO_HAPTIC *dev)
 static bool whap_set_gain(ALLEGRO_HAPTIC *dev, double gain)
 {
    ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(dev);
-   (void) whap, (void) gain;
-   return false;
+   bool ok = whap_set_dinput_device_gain(whap->device, gain);
+   if (ok) {
+     whap->parent.gain = gain;
+   } else {
+     whap->parent.gain = 1.0;
+   } 
+   return ok;
 }
 
 
-double whap_get_autocenter (ALLEGRO_HAPTIC * dev) 
+double whap_get_autocenter(ALLEGRO_HAPTIC * dev) 
 {
    ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(dev);
-   /* Unfortunately there seems to be no API to GET gain, only to set?!
-    * So, return the stored gain.
-    */
-   return whap->parent.gain;
+   /* Return the stored autocenter value. It's easiest like that. */
+   return whap->parent.autocenter;
 }
 
 
 static bool whap_set_autocenter(ALLEGRO_HAPTIC *dev, double intensity)
 {
    ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(dev);
-   (void) whap, (void) intensity;
-   return false;
+   bool ok = whap_set_dinput_device_autocenter(whap->device, intensity);
+   if (ok) {
+     whap->parent.autocenter = intensity;
+   } else {
+     whap->parent.autocenter = 0.0;
+   } 
+   return ok;
 }
 
 static int whap_get_num_effects(ALLEGRO_HAPTIC *dev)
@@ -814,80 +998,159 @@ static bool whap_is_effect_ok(ALLEGRO_HAPTIC *haptic,
 
    caps = al_get_haptic_capabilities(haptic);
    if (caps & effect->type) {
-      return true; // whap_effect2lin(&leff, effect);
+      return true; 
    }
+   /* XXX: should do more checking here? */
    return false;
 }
 
 
+/*
 static double whap_effect_duration(ALLEGRO_HAPTIC_EFFECT *effect)
 {
    return effect->replay.delay + effect->replay.length;
 }
-
+*/
 
 static bool whap_upload_effect(ALLEGRO_HAPTIC *dev,
    ALLEGRO_HAPTIC_EFFECT *effect, ALLEGRO_HAPTIC_EFFECT_ID *id)
 {
+   HRESULT ret;
    ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(dev);
-
+   ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff = NULL;
+   
    ASSERT(dev);
    ASSERT(id);
    ASSERT(effect);
-
-   /* Set id's values to indicate failure. */
+   
+   /* Set id's values to indicate failure beforehand. */
    id->_haptic = NULL;
    id->_id     = -1;
    id->_handle = -1;
-   return false;
+
+   al_lock_mutex(haptic_mutex);
+   
+   /* Look for a free haptic effect slot. */
+   weff = whap_get_available_effect(whap);
+   /* No more space for an effect. */
+   if (!weff) {
+      ALLEGRO_WARN("No free effect slot.");
+      al_unlock_mutex(haptic_mutex);
+      return false;
+   }  
+  
+   
+   /* Convert the haptic effect to a windows specific one. */
+   if (!whap_effect2win(weff, effect, whap)) {
+      whap_release_effect_windows(weff); /* free effect again. */
+      ALLEGRO_WARN("Could not conver effect.");      
+      al_unlock_mutex(haptic_mutex);
+      return false;
+   }
+   
+   /* Create the effect. */
+   ret = IDirectInputDevice8_CreateEffect(whap->device, (*weff->guid),
+                                          &weff->effect,
+                                          &weff->ref   , NULL);
+   if (FAILED(ret)) {
+      whap_release_effect_windows(weff); /* free effect again. */
+      ALLEGRO_WARN("Could not create effect.");
+      al_unlock_mutex(haptic_mutex);
+      return false;
+   } 
+   
+   /* Upload the effect to the device. */
+   ret = IDirectInputEffect_Download(weff->ref);
+   
+   if (FAILED(ret)) {
+      whap_release_effect_windows(weff); /* free effect again. */
+      ALLEGRO_WARN("Could not create effect.");
+      al_unlock_mutex(haptic_mutex);
+      return false;
+   } 
+   
+   
+   
+   al_unlock_mutex(haptic_mutex);
+   return true;
 }
 
 
 static bool whap_play_effect(ALLEGRO_HAPTIC_EFFECT_ID *id, int loops)
 {
+   HRESULT res;
    ALLEGRO_HAPTIC_WINDOWS *whap = (ALLEGRO_HAPTIC_WINDOWS *) id->_haptic;
-   /*
-   struct input_event play;
-   int fd;
-   double now;
-   double duration;
-   */
-
+   ALLEGRO_HAPTIC_EFFECT_WINDOWS *weff; 
    if (!whap)
       return false;
-
-   return false;
+   weff = whap->effects + id->_id;
+   
+   res = IDirectInputEffect_Start(weff->ref, loops, 0);  
+   if(FAILED(res)) {
+      ALLEGRO_WARN("Failed to play an effect."); 
+      return false;
+   }
+   id->_playing = true;
+   // id->_
+   
+   return true;
 }
 
 
 static bool whap_stop_effect(ALLEGRO_HAPTIC_EFFECT_ID *id)
-{
+{   
+   HRESULT res;
    ALLEGRO_HAPTIC_WINDOWS *whap = (ALLEGRO_HAPTIC_WINDOWS *) id->_haptic;
- 
+   ALLEGRO_HAPTIC_EFFECT_WINDOWS *weff; 
+   
    if (!whap)
       return false;
-   return false;
+   weff = whap->effects + id->_id;
+   
+   res = IDirectInputEffect_Stop(weff->ref);  
+   if(FAILED(res)) {
+      ALLEGRO_WARN("Failed to play an effect."); 
+      return false;
+   }
+   id->_playing = false;
+   
+   
+   return true;
 }
 
 
 static bool whap_is_effect_playing(ALLEGRO_HAPTIC_EFFECT_ID *id)
 {
    ASSERT(id);
-
-   /* 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.
-    */
-   return (id->_playing && al_get_time() < id->_end_time);
+   HRESULT res;
+   DWORD flags;
+   ALLEGRO_HAPTIC_WINDOWS *whap = (ALLEGRO_HAPTIC_WINDOWS *) id->_haptic;
+   ALLEGRO_HAPTIC_EFFECT_WINDOWS *weff; 
+   
+   if (!whap)
+      return false;
+   weff = whap->effects + id->_id;
+   
+   res = IDirectInputEffect_GetEffectStatus(weff->ref, &flags);  
+   if(FAILED(res)) {
+      ALLEGRO_WARN("Failed to get the status of effect."); 
+      return false;
+   }
+   return ((flags & DIEGES_PLAYING) == DIEGES_PLAYING);
 }
 
 
+
 static bool whap_release_effect(ALLEGRO_HAPTIC_EFFECT_ID *id)
 {
    ALLEGRO_HAPTIC_WINDOWS *whap = (ALLEGRO_HAPTIC_WINDOWS *)id->_haptic;
-
+   ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff;
+   
+   
    whap_stop_effect(id);
-   whap->effects[id->_id].active = false; /* not in use */
-   return true;
+   
+   weff = whap->effects + id->_id;
+   return whap_release_effect_windows(weff);
 }
 
 
@@ -895,10 +1158,16 @@ static bool whap_release(ALLEGRO_HAPTIC *haptic)
 {
    ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(haptic);
    ASSERT(haptic);
+   int index;
 
    if (!whap->active)
       return false;
-
+   
+   /* Release all effects for this device. */
+   for (index = 0; index < HAPTICS_EFFECTS_MAX ; index ++) {
+     whap_release_effect_windows(whap->effects + index);
+   }
+   
    whap->active = false;
    whap->device = NULL;
    return true;
-- 
1.7.10.4


From 7e399e68154bd867fb7a59ea47a9190e13a6cbd5 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Tue, 29 Apr 2014 11:48:35 +0200
Subject: [PATCH 05/15] Use a driver list for the Linux haptics system.

---
 include/allegro5/haptic.h                  |    2 ++
 include/allegro5/internal/aintern_haptic.h |   13 ++++++++++++
 src/linux/lhaptic.c                        |   19 +++++++-----------
 src/unix/uhapdrv.c                         |   30 ++++++++++++++++++++++++++++
 src/x/xsystem.c                            |    6 +-----
 5 files changed, 53 insertions(+), 17 deletions(-)
 create mode 100644 src/unix/uhapdrv.c

diff --git a/include/allegro5/haptic.h b/include/allegro5/haptic.h
index 1db0aa5..f97d4df 100644
--- a/include/allegro5/haptic.h
+++ b/include/allegro5/haptic.h
@@ -186,6 +186,7 @@ struct ALLEGRO_HAPTIC_EFFECT_ID
    ALLEGRO_HAPTIC *_haptic;
    int _id;
    int _handle;
+   void * _pointer;
    double _effect_duration;
    bool _playing;
    double _start_time;
@@ -230,6 +231,7 @@ AL_FUNC(bool, al_upload_and_play_haptic_effect, (ALLEGRO_HAPTIC *, ALLEGRO_HAPTI
 AL_FUNC(bool, al_stop_haptic_effect, (ALLEGRO_HAPTIC_EFFECT_ID *));
 AL_FUNC(bool, al_is_haptic_effect_playing, (ALLEGRO_HAPTIC_EFFECT_ID *));
 AL_FUNC(bool, al_release_haptic_effect, (ALLEGRO_HAPTIC_EFFECT_ID *));
+AL_FUNC(double, al_get_haptic_effect_duration, (ALLEGRO_HAPTIC_EFFECT *));
 AL_FUNC(bool, al_rumble_haptic, (ALLEGRO_HAPTIC *, double, double, ALLEGRO_HAPTIC_EFFECT_ID *));
 
 
diff --git a/include/allegro5/internal/aintern_haptic.h b/include/allegro5/internal/aintern_haptic.h
index afaefeb..27f7c30 100644
--- a/include/allegro5/internal/aintern_haptic.h
+++ b/include/allegro5/internal/aintern_haptic.h
@@ -68,6 +68,19 @@ struct ALLEGRO_HAPTIC
    double autocenter;
 };
 
+/* Haptic diver list. */
+extern _AL_DRIVER_INFO _al_haptic_driver_list[];
+
+/* 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 }        \
+   };
+
+
 
 #ifdef __cplusplus
    }
diff --git a/src/linux/lhaptic.c b/src/linux/lhaptic.c
index 4af4c13..d409e8d 100644
--- a/src/linux/lhaptic.c
+++ b/src/linux/lhaptic.c
@@ -90,7 +90,7 @@ static bool lhap_is_effect_playing(ALLEGRO_HAPTIC_EFFECT_ID *id);
 static bool lhap_release_effect(ALLEGRO_HAPTIC_EFFECT_ID *id);
 
 static double lhap_get_autocenter(ALLEGRO_HAPTIC *dev);
-static double lhap_set_autocenter(ALLEGRO_HAPTIC *dev, double);
+static bool lhap_set_autocenter(ALLEGRO_HAPTIC *dev, double);
 
 ALLEGRO_HAPTIC_DRIVER _al_hapdrv_linux =
 {
@@ -604,10 +604,11 @@ static double lhap_get_gain(ALLEGRO_HAPTIC *dev)
 {
    ALLEGRO_HAPTIC_LINUX *lhap = lhap_from_al(dev);
    (void)dev;
-   if(!al_is_haptic_capable(hap, ALLEGRO_HAPTIC_GAIN)) { 
+   
+   if(!al_is_haptic_capable(dev, ALLEGRO_HAPTIC_GAIN)) { 
      return 0.0;  
    } 
-
+   
    /* Unfortunately there seems to be no API to GET gain, only to set?!
     * So, return the stored gain.
     */
@@ -641,7 +642,7 @@ static bool lhap_set_autocenter(ALLEGRO_HAPTIC *dev, double autocenter)
    timerclear(&ie.time);
    ie.type = EV_FF;
    ie.code = FF_AUTOCENTER;
-   ie.value = (__s32) ((double)0xFFFF * gain);
+   ie.value = (__s32) ((double)0xFFFF * autocenter);
    if (write(lhap->fd, &ie, sizeof(ie)) < 0) {
       return false;
    }
@@ -653,7 +654,7 @@ static double lhap_get_autocenter(ALLEGRO_HAPTIC *dev)
    ALLEGRO_HAPTIC_LINUX *lhap = lhap_from_al(dev);
    (void)dev;
    
-   if(!al_is_haptic_capable(hap, ALLEGRO_HAPTIC_AUTOCENTER)) { 
+   if(!al_is_haptic_capable(dev, ALLEGRO_HAPTIC_AUTOCENTER)) { 
      return 0.0;
    }
 
@@ -694,12 +695,6 @@ static bool lhap_is_effect_ok(ALLEGRO_HAPTIC *haptic,
 }
 
 
-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)
 {
@@ -747,7 +742,7 @@ static bool lhap_upload_effect(ALLEGRO_HAPTIC *dev,
    id->_haptic = dev;
    id->_id = found;
    id->_handle = leff.id;
-   id->_effect_duration = lhap_effect_duration(effect);
+   id->_effect_duration = al_get_haptic_effect_duration(effect);
    id->_playing = false;
 
    /* XXX should be bool or something? */
diff --git a/src/unix/uhapdrv.c b/src/unix/uhapdrv.c
new file mode 100644
index 0000000..adab45a
--- /dev/null
+++ b/src/unix/uhapdrv.c
@@ -0,0 +1,30 @@
+/*         ______   ___    ___ 
+ *        /\  _  \ /\_ \  /\_ \ 
+ *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
+ *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
+ *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
+ *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
+ *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
+ *                                           /\____/
+ *                                           \_/__/
+ *
+ *      List of Unix joystick drivers.
+ *
+ *      By Shawn Hargreaves.
+ *
+ *      See readme.txt for copyright information.
+ */
+
+
+#include "allegro5/allegro.h"
+#include "allegro5/platform/aintunix.h"
+#include "allegro5/internal/aintern.h"
+#include "allegro5/internal/aintern_joystick.h"
+
+
+
+_AL_BEGIN_JOYSTICK_DRIVER_LIST
+#if defined ALLEGRO_HAVE_LINUX_INPUT_H && (defined ALLEGRO_WITH_XWINDOWS || defined ALLEGRO_RASPBERRYPI)
+   { _ALLEGRO_JOYDRV_LINUX,   &_al_joydrv_linux,   true  },
+#endif
+_AL_END_JOYSTICK_DRIVER_LIST
diff --git a/src/x/xsystem.c b/src/x/xsystem.c
index 70614a6..cc51fba 100644
--- a/src/x/xsystem.c
+++ b/src/x/xsystem.c
@@ -175,11 +175,7 @@ static ALLEGRO_JOYSTICK_DRIVER *xglx_get_joystick_driver(void)
 
 static ALLEGRO_HAPTIC_DRIVER *xglx_get_haptic_driver(void)
 {
-#ifdef ALLEGRO_HAVE_LINUX_INPUT_H
-   return &_al_hapdrv_linux;
-#else
-   return NULL;
-#endif
+   return _al_haptic_driver_list[0].driver;
 }
 
 static int xglx_get_num_video_adapters(void)
-- 
1.7.10.4


From cdfc648c0990acdda3fd5238cf72455d9a37a882 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Tue, 29 Apr 2014 11:49:12 +0200
Subject: [PATCH 06/15] Use a driver list for the Linux haptics system.

---
 cmake/FileList.cmake |    1 +
 1 file changed, 1 insertion(+)

diff --git a/cmake/FileList.cmake b/cmake/FileList.cmake
index 9e4de24..31ca116 100644
--- a/cmake/FileList.cmake
+++ b/cmake/FileList.cmake
@@ -97,6 +97,7 @@ set(ALLEGRO_SRC_UNIX_FILES
     src/unix/udrvlist.c
     src/unix/ufdwatch.c
     src/unix/ugfxdrv.c
+    src/unix/uhapdrv.c
     src/unix/ujoydrv.c
     src/unix/ukeybd.c
     src/unix/umouse.c
-- 
1.7.10.4


From b05a95c5508cc24689d55443c4615ce3b2c56f30 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Tue, 29 Apr 2014 11:51:07 +0200
Subject: [PATCH 07/15] Add al_get_haptic_effect_duration to the API since it
 is reused in the drivers, and also since it could be
 useful for the end user. Document the function as
 well, and make some small updates to the haptics
 documentation.

---
 docs/src/refman/haptic.txt |   26 ++++++++++++++++++--------
 src/haptic.c               |    6 ++++++
 2 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/docs/src/refman/haptic.txt b/docs/src/refman/haptic.txt
index 59a1f4f..2c1edd5 100644
--- a/docs/src/refman/haptic.txt
+++ b/docs/src/refman/haptic.txt
@@ -95,9 +95,11 @@ direction
 
     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 π means in the direction away from the user.
+    holds the device, expressed in radians. This angle increases clockwise
+    away from the user. So, an effect with an angle 0.0 takes place in the 
+    direction of the user of the haptic device, an angle of π/2 is
+    to the left of the user, an angle of π means the direction away from 
+    the user, and an angle of 3π/2 means to the right of the user.
 
     If [al_get_haptic_capabilities] has the flag ALLEGRO_HAPTIC_ANGLE set,
     then setting `direction.angle` is supported. Otherwise, it is
@@ -117,9 +119,9 @@ direction
     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 +π/2 means the
-    effect plays back vertically above the user plane, and an azimuth -π/2
-    means the effect plays back vertically below the user plane.
+    plane in which the user is holding the device, an azimuth +π/2 means 
+    the effect plays back vertically above the user plane, and an azimuth 
+    -π/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
@@ -498,11 +500,19 @@ from [al_upload_haptic_effect], [al_upload_and_play_haptic_effect] or
 
 Since: 5.1.8
 
+## API: al_get_haptic_effect_duration
+
+Returns the estimated duration of the given haptic effect. This is merely 
+a convenience function for now, as it returns the sum of 
+`effect.replay.length` and `effect.replay.data`.  
+
+Since: 5.1.9
+
 ## 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],
+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].
 
 Since: 5.1.8
diff --git a/src/haptic.c b/src/haptic.c
index 83c3969..739bbe8 100644
--- a/src/haptic.c
+++ b/src/haptic.c
@@ -329,6 +329,12 @@ bool al_is_haptic_effect_playing(ALLEGRO_HAPTIC_EFFECT_ID *id)
    return haptic_driver->is_effect_playing(id);
 }
 
+/* Function: al_get_haptic_effect_duration
+ */
+double al_get_haptic_effect_duration(ALLEGRO_HAPTIC_EFFECT * effect)
+{
+  return effect->replay.delay + effect->replay.length;
+}
 
 /* Function: al_rumble_haptic
  */
-- 
1.7.10.4


From 1239e6c0aedcb153c7f53a3602b86bf91034f4f0 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Tue, 29 Apr 2014 11:52:44 +0200
Subject: [PATCH 08/15] Forgot to add linux haptic driver list.

---
 src/unix/uhapdrv.c |   12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/unix/uhapdrv.c b/src/unix/uhapdrv.c
index adab45a..eae23e6 100644
--- a/src/unix/uhapdrv.c
+++ b/src/unix/uhapdrv.c
@@ -8,9 +8,9 @@
  *                                           /\____/
  *                                           \_/__/
  *
- *      List of Unix joystick drivers.
+ *      List of Unix haptic drivers.
  *
- *      By Shawn Hargreaves.
+ *      By Beoran.
  *
  *      See readme.txt for copyright information.
  */
@@ -19,12 +19,12 @@
 #include "allegro5/allegro.h"
 #include "allegro5/platform/aintunix.h"
 #include "allegro5/internal/aintern.h"
-#include "allegro5/internal/aintern_joystick.h"
+#include "allegro5/internal/aintern_haptic.h"
 
 
 
-_AL_BEGIN_JOYSTICK_DRIVER_LIST
+_AL_BEGIN_HAPTIC_DRIVER_LIST
 #if defined ALLEGRO_HAVE_LINUX_INPUT_H && (defined ALLEGRO_WITH_XWINDOWS || defined ALLEGRO_RASPBERRYPI)
-   { _ALLEGRO_JOYDRV_LINUX,   &_al_joydrv_linux,   true  },
+   { _ALLEGRO_HAPDRV_LINUX,   &_al_hapdrv_linux,   true  },
 #endif
-_AL_END_JOYSTICK_DRIVER_LIST
+_AL_END_HAPTIC_DRIVER_LIST
-- 
1.7.10.4


From 255c269443e7e49aba1734916e8d2097545a2bb6 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Tue, 29 Apr 2014 11:56:43 +0200
Subject: [PATCH 09/15] Use a driver list in Windows haptics driver,
 refactored and bugfixes.

---
 include/allegro5/platform/alwin.h |   23 +++++++-
 src/win/whaptic.c                 |    5 ++
 src/win/whaptic.cpp               |  106 +++++++++++++++++++++----------------
 src/win/wsystem.c                 |    6 +++
 4 files changed, 93 insertions(+), 47 deletions(-)

diff --git a/include/allegro5/platform/alwin.h b/include/allegro5/platform/alwin.h
index d8f3a07..d19b3bf 100644
--- a/include/allegro5/platform/alwin.h
+++ b/include/allegro5/platform/alwin.h
@@ -63,5 +63,24 @@ AL_VAR(struct ALLEGRO_JOYSTICK_DRIVER, _al_joydrv_directx);
    { AL_JOY_TYPE_DIRECTX,  &_al_joydrv_directx,    true  },
 
 
-/** HAPTIC driver ID */
-#define _ALLEGRO_HAPDRV_WINDOWS    AL_ID('W','I','N','H')
+/*******************************************/
+/************ haptic drivers   *************/
+/*******************************************/
+
+#define AL_HAPTIC_TYPE_DIRECTX   AL_ID('D','X','H','D')
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+AL_VAR(struct ALLEGRO_HAPTIC_DRIVER, _al_hapdrv_directx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#define _AL_HAPTIC_DRIVER_DIRECTX                                     \
+   { AL__TYPE_DIRECTX,  &_al_hapdrv_directx,    true  },
+
+
+
diff --git a/src/win/whaptic.c b/src/win/whaptic.c
index ca88815..a9293e1 100644
--- a/src/win/whaptic.c
+++ b/src/win/whaptic.c
@@ -25,5 +25,10 @@
 #error something is wrong with the makefile
 #endif
 
+/* Construct the DirectInput haptics driver list */
+_AL_BEGIN_HAPTIC_DRIVER_LIST
+
+_AL_END_HAPTIC_DRIVER_LIST
+
 
 
diff --git a/src/win/whaptic.cpp b/src/win/whaptic.cpp
index fadf83c..e05c177 100644
--- a/src/win/whaptic.cpp
+++ b/src/win/whaptic.cpp
@@ -78,6 +78,7 @@ typedef union {
  */
 typedef struct 
 {
+   int                                  id;
    bool                                 active;
    DIEFFECT                             effect;
    DIENVELOPE                           envelope;
@@ -152,9 +153,9 @@ static bool whap_release_effect(ALLEGRO_HAPTIC_EFFECT_ID *id);
 static double whap_get_autocenter(ALLEGRO_HAPTIC *dev);
 static bool whap_set_autocenter(ALLEGRO_HAPTIC *dev, double);
 
-ALLEGRO_HAPTIC_DRIVER _al_hapdrv_windows =
-{
-   _ALLEGRO_HAPDRV_WINDOWS,
+ALLEGRO_HAPTIC_DRIVER _al_hapdrv_directx =
+{   
+   AL_HAPTIC_TYPE_DIRECTX,
    "",
    "",
    "Windows haptic(s)",
@@ -266,7 +267,8 @@ static ALLEGRO_HAPTIC_EFFECT_WINDOWS *whap_get_available_effect(
   int i;
    for (i = 0; i < al_get_num_haptic_effects(&whap->parent); i++) {
       if (!whap->effects[i].active) {
-         weff = whap->effects + i;
+         weff         = whap->effects + i;
+         weff->id     = i;
          weff->active = true;
          weff->ref    = NULL;
          return weff;
@@ -279,6 +281,7 @@ static ALLEGRO_HAPTIC_EFFECT_WINDOWS *whap_get_available_effect(
 /* Releases a windows haptics effect and unloads it from the device. */
 static bool whap_release_effect_windows(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff) {
    bool result = true;
+   if(!weff) return false; /* make it easy to handle alll cases later on.*/
    if(!weff->active) return false; /* already not in use, bail out. */
     
    /* Unload the effect from the device. */
@@ -292,9 +295,9 @@ static bool whap_release_effect_windows(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff) {
    }
    /* Custom force needs to clean up it's data. */
    if (weff->guid == &GUID_CustomForce) { 
-    al_free(weff->parameter.custom.rglForceData);
-    weff->parameter.custom.rglForceData = NULL;
-   }   
+      al_free(weff->parameter.custom.rglForceData);
+      weff->parameter.custom.rglForceData = NULL;
+   }  
    weff->active = false; /* not in use */
    weff->ref    = NULL; /* No reference to effect anymore. */  
    return result;
@@ -1011,11 +1014,41 @@ static double whap_effect_duration(ALLEGRO_HAPTIC_EFFECT *effect)
    return effect->replay.delay + effect->replay.length;
 }
 */
+static bool whap_upload_effect_helper
+  (ALLEGRO_HAPTIC_WINDOWS         * whap, 
+   ALLEGRO_HAPTIC_EFFECT_WINDOWS  * weff,
+   ALLEGRO_HAPTIC_EFFECT          * effect
+  ) 
+{
+   HRESULT ret;
+   if (!whap_effect2win(weff, effect, whap)) { 
+      ALLEGRO_WARN("Could not convert haptic effect.");      
+      return false;
+   }
+    
+   /* Create the effect. */
+   ret = IDirectInputDevice8_CreateEffect(whap->device, (*weff->guid),
+                                          &weff->effect,
+                                          &weff->ref   , NULL);
+   if (FAILED(ret)) {
+      ALLEGRO_WARN("Could not create haptic effect.");
+      return false;
+   } 
+  
+     /* Upload the effect to the device. */
+   ret = IDirectInputEffect_Download(weff->ref);   
+   if (FAILED(ret)) {
+      ALLEGRO_WARN("Could not upload haptic effect.");
+      return false;
+   }   
+   
+   return true;
+}
 
 static bool whap_upload_effect(ALLEGRO_HAPTIC *dev,
    ALLEGRO_HAPTIC_EFFECT *effect, ALLEGRO_HAPTIC_EFFECT_ID *id)
 {
-   HRESULT ret;
+   bool ok;
    ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(dev);
    ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff = NULL;
    
@@ -1024,10 +1057,12 @@ static bool whap_upload_effect(ALLEGRO_HAPTIC *dev,
    ASSERT(effect);
    
    /* Set id's values to indicate failure beforehand. */
-   id->_haptic = NULL;
-   id->_id     = -1;
-   id->_handle = -1;
-
+   id->_haptic          = NULL;
+   id->_id              = -1;
+   id->_pointer         = NULL;
+   id->_playing         = false;
+   id->_effect_duration = 0.0;
+   
    al_lock_mutex(haptic_mutex);
    
    /* Look for a free haptic effect slot. */
@@ -1035,44 +1070,19 @@ static bool whap_upload_effect(ALLEGRO_HAPTIC *dev,
    /* No more space for an effect. */
    if (!weff) {
       ALLEGRO_WARN("No free effect slot.");
-      al_unlock_mutex(haptic_mutex);
       return false;
    }  
-  
-   
-   /* Convert the haptic effect to a windows specific one. */
-   if (!whap_effect2win(weff, effect, whap)) {
-      whap_release_effect_windows(weff); /* free effect again. */
-      ALLEGRO_WARN("Could not conver effect.");      
-      al_unlock_mutex(haptic_mutex);
-      return false;
-   }
-   
-   /* Create the effect. */
-   ret = IDirectInputDevice8_CreateEffect(whap->device, (*weff->guid),
-                                          &weff->effect,
-                                          &weff->ref   , NULL);
-   if (FAILED(ret)) {
-      whap_release_effect_windows(weff); /* free effect again. */
-      ALLEGRO_WARN("Could not create effect.");
-      al_unlock_mutex(haptic_mutex);
-      return false;
-   } 
-   
-   /* Upload the effect to the device. */
-   ret = IDirectInputEffect_Download(weff->ref);
-   
-   if (FAILED(ret)) {
-      whap_release_effect_windows(weff); /* free effect again. */
-      ALLEGRO_WARN("Could not create effect.");
-      al_unlock_mutex(haptic_mutex);
-      return false;
+   ok = whap_upload_effect_helper(whap, weff, effect);
+   if (ok) {
+      /* set ID handle to signify success */
+      id->_haptic  = dev;
+      id->_pointer = weff;
+      id->_id      = weff->id;      
+      id->_effect_duration = al_get_haptic_effect_duration(effect);
    } 
    
-   
-   
    al_unlock_mutex(haptic_mutex);
-   return true;
+   return ok;
 }
 
 
@@ -1094,6 +1104,12 @@ static bool whap_play_effect(ALLEGRO_HAPTIC_EFFECT_ID *id, int loops)
    // id->_
    
    return true;
+   
+   
+   
+
+   
+   
 }
 
 
diff --git a/src/win/wsystem.c b/src/win/wsystem.c
index 5348bcf..6907108 100644
--- a/src/win/wsystem.c
+++ b/src/win/wsystem.c
@@ -309,6 +309,11 @@ static ALLEGRO_JOYSTICK_DRIVER *win_get_joystick_driver(void)
    return _al_joystick_driver_list[0].driver;
 }
 
+static ALLEGRO_HAPTIC_DRIVER *win_get_haptic_driver(void)
+{
+   return _al_haptic_driver_list[0].driver;
+}
+
 static int win_get_num_display_modes(void)
 {
    int format = _al_deduce_color_format(_al_get_new_display_settings());
@@ -693,6 +698,7 @@ static ALLEGRO_SYSTEM_INTERFACE *_al_system_win_driver(void)
    vt->get_keyboard_driver = win_get_keyboard_driver;
    vt->get_mouse_driver = win_get_mouse_driver;
    vt->get_touch_input_driver = win_get_touch_input_driver;
+   vt->get_haptic_driver = win_get_haptic_driver;   
    vt->get_joystick_driver = win_get_joystick_driver;
    vt->get_num_display_modes = win_get_num_display_modes;
    vt->get_display_mode = win_get_display_mode;
-- 
1.7.10.4


From 1899ebb29983d5fb5bd5ea78dff863fbf0494750 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Tue, 29 Apr 2014 11:57:51 +0200
Subject: [PATCH 10/15] Enhanced haptics example program. Can now set
 autocentering, and detects hot-plugged joysticks as
 well.

---
 examples/ex_haptic2.cpp |  137 +++++++++++++++++++++++++++++++++++------------
 1 file changed, 102 insertions(+), 35 deletions(-)

diff --git a/examples/ex_haptic2.cpp b/examples/ex_haptic2.cpp
index 1960492..8c7cdf2 100644
--- a/examples/ex_haptic2.cpp
+++ b/examples/ex_haptic2.cpp
@@ -28,6 +28,45 @@ struct Haptic
 
 static Haptic haptics[EX_MAX_HAPTICS];
 static int num_haptics = 0;
+static ALLEGRO_EVENT_QUEUE * joystick_queue;
+
+static void release_all_haptics() {
+   for (int i = 0; i < num_haptics; i++) {    
+      al_release_haptic(haptics[i].haptic);
+      haptics[i].haptic = NULL;
+   }  
+}
+
+static void get_all_haptics() {
+   num_haptics = 0;
+   ALLEGRO_DISPLAY * display = al_get_current_display();
+
+   if (al_is_display_haptic(display)) {
+      haptics[num_haptics].haptic = al_get_haptic_from_display(display);
+      if (haptics[num_haptics].haptic) {
+         haptics[num_haptics].name = (const char *)"display";
+         haptics[num_haptics].playing = false;
+         num_haptics++;
+      }
+   }
+
+   for (int i = 0;
+      num_haptics < EX_MAX_HAPTICS && i < al_get_num_joysticks();
+      i++)
+   {
+      ALLEGRO_JOYSTICK *joy = al_get_joystick(i);
+      if (al_is_joystick_haptic(joy)) {
+         haptics[num_haptics].haptic = al_get_haptic_from_joystick(joy);
+         if (haptics[num_haptics].haptic) {
+            const char *name = al_get_joystick_name(joy);
+            haptics[num_haptics].name = (const char *)name;
+            haptics[num_haptics].playing = false;
+            num_haptics++;
+         }
+      }
+   }
+
+}
 
 struct CapacityName
 {
@@ -205,6 +244,9 @@ class Prog: public CanStopAndPlay
 
    HSlider gain_slider;
    Label gain_label;
+   
+   HSlider autocenter_slider;
+   Label autocenter_label;
 
    Label message_label;
    Label message_label_label;
@@ -293,6 +335,8 @@ Prog::Prog(const Theme & theme, ALLEGRO_DISPLAY *display) :
    weak_magnitude_label("Weak Magnitude", false),
    gain_slider(10, 10),
    gain_label("Gain"),
+   autocenter_slider(0, 10),
+   autocenter_label("Autocenter"),
    message_label("Ready.", false),
    message_label_label("Status", false),
    play_button(this),
@@ -328,6 +372,10 @@ Prog::Prog(const Theme & theme, ALLEGRO_DISPLAY *display) :
    d.add(loops_slider, 7, 12, 6, 1);
    d.add(gain_label, 13, 11, 7, 1);
    d.add(gain_slider, 13, 12, 7, 1);
+   
+   d.add(autocenter_label, 13, 13, 7, 1);
+   d.add(autocenter_slider, 13, 14, 7, 1);
+
 
    d.add(envelope_label, 0, 15, 9, 1);
    d.add(attack_length_label, 0, 16, 3, 1);
@@ -392,6 +440,7 @@ Prog::Prog(const Theme & theme, ALLEGRO_DISPLAY *display) :
 
    d.add(play_button, 6, 38, 3, 2);
    d.add(stop_button, 12, 38, 3, 2);
+   
 }
 
 void Prog::update()
@@ -407,19 +456,42 @@ void Prog::update()
          log_printf("Play done on %s\n", last_haptic->name);
       }
    }
+   
+   ALLEGRO_EVENT e;
+   
+   /* Check for hot plugging*/
+   while(al_get_next_event(joystick_queue, &e)) {
+     /* clear, reconfigure and fetch haptics again. */
+     if (e.type == ALLEGRO_EVENT_JOYSTICK_CONFIGURATION) {
+       al_reconfigure_joysticks(); 
+       release_all_haptics();
+       get_all_haptics();
+       device_list.clear_items();
+       for (int i = 0; i < num_haptics; i++) {
+          device_list.append_item(haptics[i].name);
+       }
+       log_printf("Hot plugging detected...\n");  
+       message_label.set_text("Hot Plugging...");
+       play_button.set_disabled(false);
+       d.request_draw();
+     }
+   }
 
    /* Update availability of controls based on capabilities. */
    int devno = device_list.get_cur_value();
    Haptic *dev = haptics + devno;
    if (dev && dev->haptic) {
       if (dev != show_haptic) {
+         play_button.set_disabled(false);
          update_controls(dev);
          show_haptic = dev;
-      }
-   }
-   else {
+         message_label.set_text("Haptic Device Found.");
+         d.request_draw();
+      }     
+   } else {
       play_button.set_disabled(true);
       message_label.set_text("No Haptic Device.");
+      d.request_draw();
    }
 }
 
@@ -447,12 +519,20 @@ void Prog::run()
 void Prog::update_controls(Haptic *dev)
 {
    /* Take a deep breath, here we go... */
-   bool condition, envelope, periodic;
-   int cap = al_get_haptic_capabilities(dev->haptic);
+   bool condition, envelope, periodic;      
+   int cap = 0;
+   if (dev) { 
+     cap = al_get_haptic_capabilities(dev->haptic);
+   }
 
    /* Gain capability */
    gain_slider.set_disabled(!TEST_CAP(cap, ALLEGRO_HAPTIC_GAIN));
    gain_label.set_disabled(!TEST_CAP(cap, ALLEGRO_HAPTIC_GAIN));
+   
+   /* Autocenter capability */
+   autocenter_slider.set_disabled(!TEST_CAP(cap, ALLEGRO_HAPTIC_AUTOCENTER));
+   autocenter_label.set_disabled(!TEST_CAP(cap, ALLEGRO_HAPTIC_AUTOCENTER));
+   
 
    /* Envelope related capabilities and sliders. */
    envelope = TEST_CAP(cap, ALLEGRO_HAPTIC_PERIODIC) ||
@@ -625,6 +705,11 @@ void Prog::on_play()
    /* First set gain. */
    double gain = slider_to_magnitude(gain_slider);
    al_set_haptic_gain(haptic->haptic, gain);
+   
+   /* Set autocentering. */
+   double autocenter = slider_to_magnitude(autocenter_slider);
+   al_set_haptic_autocenter(haptic->haptic, autocenter);
+
 
    /* Now fill in the effect struct. */
    int type = name_to_cap(type_list.get_selected_item_text());
@@ -776,33 +861,16 @@ int main(int argc, char *argv[])
          abort_example("Could not create builtin font.\n");
       }
    }
-
-   num_haptics = 0;
-
-   if (al_is_display_haptic(display)) {
-      haptics[num_haptics].haptic = al_get_haptic_from_display(display);
-      if (haptics[num_haptics].haptic) {
-         haptics[num_haptics].name = (const char *)"display";
-         haptics[num_haptics].playing = false;
-         num_haptics++;
-      }
+   
+   joystick_queue = al_create_event_queue();
+   if (!joystick_queue) {
+      abort_example("Could not create joystick event queue font.\n");
    }
+   
+   al_register_event_source(joystick_queue, al_get_joystick_event_source());
 
-   for (int i = 0;
-      num_haptics < EX_MAX_HAPTICS && i < al_get_num_joysticks();
-      i++)
-   {
-      ALLEGRO_JOYSTICK *joy = al_get_joystick(i);
-      if (al_is_joystick_haptic(joy)) {
-         haptics[num_haptics].haptic = al_get_haptic_from_joystick(joy);
-         if (haptics[num_haptics].haptic) {
-            const char *name = al_get_joystick_name(joy);
-            haptics[num_haptics].name = (const char *)name;
-            haptics[num_haptics].playing = false;
-            num_haptics++;
-         }
-      }
-   }
+   
+   get_all_haptics();
 
    /* Don't remove these braces. */
    {
@@ -810,13 +878,12 @@ int main(int argc, char *argv[])
       Prog prog(theme, display);
       prog.run();
    }
-
-   for (int i = 0; i < num_haptics; i++) {
-      al_release_haptic(haptics[i].haptic);
-   }
+   
+   release_all_haptics();
+   al_destroy_event_queue(joystick_queue); 
 
    close_log(false);
-
+   
    al_destroy_font(font);
 
    return 0;
-- 
1.7.10.4


From 4a44d22fe335cb7bfd344f56a89e683ae41a7685 Mon Sep 17 00:00:00 2001
From: Bjorn De Meyer <bjorn.de.meyer@xxxxxxxxx>
Date: Mon, 5 May 2014 11:55:35 +0200
Subject: [PATCH 11/15] Bugfix: make the haptic driver load propely, the
 driver list ws incorrect. Also add a bit more debug
 reporting.

---
 include/allegro5/internal/aintern_haptic.h |    6 +-
 include/allegro5/platform/alwin.h          |    4 +-
 src/win/whaptic.c                          |    8 +-
 src/win/whaptic.cpp                        |  365 ++++++++++++++--------------
 src/win/wsystem.c                          |    5 +-
 5 files changed, 196 insertions(+), 192 deletions(-)

diff --git a/include/allegro5/internal/aintern_haptic.h b/include/allegro5/internal/aintern_haptic.h
index 27f7c30..d236ea9 100644
--- a/include/allegro5/internal/aintern_haptic.h
+++ b/include/allegro5/internal/aintern_haptic.h
@@ -47,7 +47,7 @@ typedef struct ALLEGRO_HAPTIC_DRIVER
    AL_METHOD(bool, release_effect, (ALLEGRO_HAPTIC_EFFECT_ID *));
    AL_METHOD(bool, release, (ALLEGRO_HAPTIC *));
    AL_METHOD(double, get_autocenter, (ALLEGRO_HAPTIC *));
-   AL_METHOD(bool, set_autocenter, (ALLEGRO_HAPTIC *, double));  
+   AL_METHOD(bool, set_autocenter, (ALLEGRO_HAPTIC *, double));
 } ALLEGRO_HAPTIC_DRIVER;
 
 
@@ -69,11 +69,11 @@ struct ALLEGRO_HAPTIC
 };
 
 /* Haptic diver list. */
-extern _AL_DRIVER_INFO _al_haptic_driver_list[];
+extern const _AL_DRIVER_INFO _al_haptic_driver_list[];
 
 /* Macros for constructing the driver list */
 #define _AL_BEGIN_HAPTIC_DRIVER_LIST                            \
-   _AL_DRIVER_INFO _al_haptic_driver_list[] =                  \
+   const _AL_DRIVER_INFO _al_haptic_driver_list[] =                  \
    {
 
 #define _AL_END_HAPTIC_DRIVER_LIST                              \
diff --git a/include/allegro5/platform/alwin.h b/include/allegro5/platform/alwin.h
index d19b3bf..b699113 100644
--- a/include/allegro5/platform/alwin.h
+++ b/include/allegro5/platform/alwin.h
@@ -1,6 +1,6 @@
 /*         ______   ___    ___
  *        /\  _  \ /\_ \  /\_ \
- *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
+ *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
@@ -80,7 +80,7 @@ AL_VAR(struct ALLEGRO_HAPTIC_DRIVER, _al_hapdrv_directx);
 #endif
 
 #define _AL_HAPTIC_DRIVER_DIRECTX                                     \
-   { AL__TYPE_DIRECTX,  &_al_hapdrv_directx,    true  },
+   { AL_HAPTIC_TYPE_DIRECTX,  &_al_hapdrv_directx,    true  },
 
 
 
diff --git a/src/win/whaptic.c b/src/win/whaptic.c
index a9293e1..35b1f34 100644
--- a/src/win/whaptic.c
+++ b/src/win/whaptic.c
@@ -1,6 +1,6 @@
-/*         ______   ___    ___ 
- *        /\  _  \ /\_ \  /\_ \ 
- *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
+/*         ______   ___    ___
+ *        /\  _  \ /\_ \  /\_ \
+ *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
@@ -27,7 +27,7 @@
 
 /* Construct the DirectInput haptics driver list */
 _AL_BEGIN_HAPTIC_DRIVER_LIST
-
+_AL_HAPTIC_DRIVER_DIRECTX
 _AL_END_HAPTIC_DRIVER_LIST
 
 
diff --git a/src/win/whaptic.cpp b/src/win/whaptic.cpp
index e05c177..7128516 100644
--- a/src/win/whaptic.cpp
+++ b/src/win/whaptic.cpp
@@ -61,7 +61,7 @@ ALLEGRO_DEBUG_CHANNEL("whaptic")
 /* Support at most 3 axes per device. */
 #define HAPTICS_AXES_MAX        3
 
-/** This union is needed to avoid 
+/** This union is needed to avoid
  dynamical memory allocation. */
 
 typedef union {
@@ -76,7 +76,7 @@ typedef union {
 /*
  * Haptic effect system data.
  */
-typedef struct 
+typedef struct
 {
    int                                  id;
    bool                                 active;
@@ -101,12 +101,12 @@ typedef struct
    DIDEVCAPS            capabilities;
    LPDIRECTINPUTDEVICE8 device8;
 
-   
+
    int flags;
    ALLEGRO_HAPTIC_EFFECT_WINDOWS effects[HAPTICS_EFFECTS_MAX];
    DWORD axes[HAPTICS_AXES_MAX];
    int naxes;
-  
+
 } ALLEGRO_HAPTIC_WINDOWS;
 
 
@@ -154,7 +154,7 @@ static double whap_get_autocenter(ALLEGRO_HAPTIC *dev);
 static bool whap_set_autocenter(ALLEGRO_HAPTIC *dev, double);
 
 ALLEGRO_HAPTIC_DRIVER _al_hapdrv_directx =
-{   
+{
    AL_HAPTIC_TYPE_DIRECTX,
    "",
    "",
@@ -188,7 +188,7 @@ ALLEGRO_HAPTIC_DRIVER _al_hapdrv_directx =
    whap_release_effect,
 
    whap_release,
-   
+
    whap_get_autocenter,
    whap_set_autocenter
 };
@@ -207,23 +207,23 @@ struct CAP_MAP {
 #define DEFINE_PRIVATE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
    static const GUID name = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }
 
-DEFINE_PRIVATE_GUID(_al_GUID_None, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 
-   
+DEFINE_PRIVATE_GUID(_al_GUID_None, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
 
 static const struct CAP_MAP cap_map[] = {
-   { { GUID_ConstantForce },    ALLEGRO_HAPTIC_CONSTANT },
-   { { GUID_Spring },           ALLEGRO_HAPTIC_SPRING },
-   { { GUID_Spring },           ALLEGRO_HAPTIC_FRICTION },
-   { { GUID_Damper },           ALLEGRO_HAPTIC_DAMPER },
-   { { GUID_Inertia },          ALLEGRO_HAPTIC_INERTIA },
-   { { GUID_RampForce },        ALLEGRO_HAPTIC_RAMP },
-   { { GUID_Square },           ALLEGRO_HAPTIC_SQUARE },
-   { { GUID_Triangle },         ALLEGRO_HAPTIC_TRIANGLE },
-   { { GUID_Sine },             ALLEGRO_HAPTIC_SINE },
-   { { GUID_SawtoothUp },       ALLEGRO_HAPTIC_SAW_UP },
-   { { GUID_SawtoothDown },     ALLEGRO_HAPTIC_SAW_DOWN },
-   { { GUID_CustomForce },      ALLEGRO_HAPTIC_CUSTOM },
-   { { _al_GUID_None    },      -1 }
+   { GUID_ConstantForce,    ALLEGRO_HAPTIC_CONSTANT },
+   { GUID_Spring,           ALLEGRO_HAPTIC_SPRING },
+   { GUID_Spring,           ALLEGRO_HAPTIC_FRICTION },
+   { GUID_Damper,           ALLEGRO_HAPTIC_DAMPER },
+   { GUID_Inertia,          ALLEGRO_HAPTIC_INERTIA },
+   { GUID_RampForce,        ALLEGRO_HAPTIC_RAMP },
+   { GUID_Square,           ALLEGRO_HAPTIC_SQUARE },
+   { GUID_Triangle,         ALLEGRO_HAPTIC_TRIANGLE },
+   { GUID_Sine,             ALLEGRO_HAPTIC_SINE },
+   { GUID_SawtoothUp,       ALLEGRO_HAPTIC_SAW_UP },
+   { GUID_SawtoothDown,     ALLEGRO_HAPTIC_SAW_DOWN },
+   { GUID_CustomForce,      ALLEGRO_HAPTIC_CUSTOM },
+   /*{ { _al_GUID_None    },      -1 }*/
 };
 
 
@@ -258,12 +258,12 @@ static ALLEGRO_HAPTIC_WINDOWS *whap_get_available_haptic(void)
    return NULL;
 }
 
- /* Look for a free haptic effect slot for a device and return it, 
-  * or NULL if exhausted. Also initializes the effect 
+ /* Look for a free haptic effect slot for a device and return it,
+  * or NULL if exhausted. Also initializes the effect
   * reference to NULL. */
 static ALLEGRO_HAPTIC_EFFECT_WINDOWS *whap_get_available_effect(
   ALLEGRO_HAPTIC_WINDOWS * whap) {
-  ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff; 
+  ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff;
   int i;
    for (i = 0; i < al_get_num_haptic_effects(&whap->parent); i++) {
       if (!whap->effects[i].active) {
@@ -283,23 +283,23 @@ static bool whap_release_effect_windows(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff) {
    bool result = true;
    if(!weff) return false; /* make it easy to handle alll cases later on.*/
    if(!weff->active) return false; /* already not in use, bail out. */
-    
+
    /* Unload the effect from the device. */
-   if(weff->ref) { 
-      HRESULT ret;     
+   if(weff->ref) {
+      HRESULT ret;
       ret = IDirectInputEffect_Unload(weff->ref);
-      if (FAILED(ret)) { 
+      if (FAILED(ret)) {
          ALLEGRO_WARN("Could not unload effect.");
          result = false;
       }
    }
    /* Custom force needs to clean up it's data. */
-   if (weff->guid == &GUID_CustomForce) { 
+   if (weff->guid == &GUID_CustomForce) {
       al_free(weff->parameter.custom.rglForceData);
       weff->parameter.custom.rglForceData = NULL;
-   }  
+   }
    weff->active = false; /* not in use */
-   weff->ref    = NULL; /* No reference to effect anymore. */  
+   weff->ref    = NULL; /* No reference to effect anymore. */
    return result;
 }
 
@@ -321,27 +321,27 @@ static void whap_exit_haptic(void)
 static bool whap_periodictype2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, ALLEGRO_HAPTIC_EFFECT * effect)
 {
   switch (effect->data.periodic.waveform) {
-    case ALLEGRO_HAPTIC_SINE: 
-      weff->guid = &GUID_Sine;      
+    case ALLEGRO_HAPTIC_SINE:
+      weff->guid = &GUID_Sine;
       return true;
-      
-    case ALLEGRO_HAPTIC_SQUARE: 
+
+    case ALLEGRO_HAPTIC_SQUARE:
       weff->guid = &GUID_Square;
       return true;
-      
-    case ALLEGRO_HAPTIC_TRIANGLE: 
+
+    case ALLEGRO_HAPTIC_TRIANGLE:
       weff->guid = &GUID_Triangle;
       return true;
-      
-    case ALLEGRO_HAPTIC_SAW_UP: 
+
+    case ALLEGRO_HAPTIC_SAW_UP:
       weff->guid = &GUID_SawtoothUp;
       return true;
-      
-    case ALLEGRO_HAPTIC_SAW_DOWN: 
+
+    case ALLEGRO_HAPTIC_SAW_DOWN:
       weff->guid = &GUID_SawtoothDown;
       return true;
-      
-    case ALLEGRO_HAPTIC_CUSTOM: 
+
+    case ALLEGRO_HAPTIC_CUSTOM:
       weff->guid = &GUID_CustomForce;
       return true;
     default:
@@ -383,7 +383,7 @@ static bool whap_type2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, ALLEGRO_HAPTIC_E
 }
 
 /* Convert the direction of the allegro effect to the windows effect*/
-static bool whap_direction2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+static bool whap_direction2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
                                ALLEGRO_HAPTIC_EFFECT * effect,
                                ALLEGRO_HAPTIC_WINDOWS * whap)
 {
@@ -392,39 +392,39 @@ static bool whap_direction2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
    weff->effect.dwFlags          = DIEFF_SPHERICAL | DIEFF_OBJECTOFFSETS;
    /* Prepare axes. */
    weff->effect.cAxes            = whap->naxes;
-   memset((void *) weff->axes      , 0, sizeof(weff->axes));   
+   memset((void *) weff->axes      , 0, sizeof(weff->axes));
    for(index = 0; index < whap->naxes; index ++) {
       weff->axes[index] = whap->axes[index];
-   }  
+   }
    weff->effect.rgdwAxes         = weff->axes;
    /* Set up directions as well.. */
    memset((void *) weff->directions, 0, sizeof(weff->directions));
-   
-   /* Zero or 1 axes means no direction anyway, start concerting from 2 
-    * available axes on the device.  
-    * 
-    * Two axes or more means we have to convert the angle of the 
+
+   /* Zero or 1 axes means no direction anyway, start concerting from 2
+    * available axes on the device.
+    *
+    * Two axes or more means we have to convert the angle of the
     * allegro effect's direction to that which DirectInput uses.
-    * Directinput starts on the right, while Allegro  API towards the 
-    * user, both clockwise. In Allegro API, right is 3*PI/2, so substract 
-    * that first, then scale to degrees * 100. 
-    */  
-   if (whap->naxes > 1) {   
+    * Directinput starts on the right, while Allegro  API towards the
+    * user, both clockwise. In Allegro API, right is 3*PI/2, so substract
+    * that first, then scale to degrees * 100.
+    */
+   if (whap->naxes > 1) {
      double angle = effect->direction.angle - ((3.0* ALLEGRO_PI) / 2.0);
      weff->directions[0] = (long) (angle * 36000.0 / (2.0*ALLEGRO_PI));
-   } 
-   
+   }
+
    /* Three axes or more. Convert the azimuth of the allegro effect.
     * The allegro azimuth is 0 in the plane of the user and increases up.
-    * The rotation in dinput is in the inverse direction (bummer) 
-    * but fortunately it starts also at 0 in the plane of the user as it 
-    * is in the allegro API.     
+    * The rotation in dinput is in the inverse direction (bummer)
+    * but fortunately it starts also at 0 in the plane of the user as it
+    * is in the allegro API.
     */
    if (whap->naxes > 2) {
-      double azimuth = - effect->direction.azimuth;      
+      double azimuth = - effect->direction.azimuth;
       weff->directions[1] = (long) (azimuth * 36000.0 / (ALLEGRO_PI/2.0));
-   }    
-   weff->effect.rglDirection     = weff->directions;  
+   }
+   weff->effect.rglDirection     = weff->directions;
    return true;
 }
 
@@ -483,7 +483,7 @@ static bool whap_phase2win(DWORD * res, double phase)
 
 
 /* Converts replay data to Widows-compatible data. */
-static bool whap_replay2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+static bool whap_replay2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
                                ALLEGRO_HAPTIC_EFFECT * effect)
 {
    return whap_time2win(&weff->effect.dwStartDelay, effect->replay.delay)
@@ -492,15 +492,15 @@ static bool whap_replay2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
 
 
 /* Converts an Allegro haptic effect envelope to directinput API. */
-static bool whap_envelope2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+static bool whap_envelope2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
                                ALLEGRO_HAPTIC_ENVELOPE * aenv)
 {
   /* Prepare envelope. */
    DIENVELOPE              * wenv = &weff->envelope;
    memset((void *) wenv, 0, sizeof(DIENVELOPE));
-   weff->envelope.dwSize         = sizeof(DIENVELOPE);  
+   weff->envelope.dwSize         = sizeof(DIENVELOPE);
    weff->effect.lpEnvelope       = wenv;
-  
+
    return whap_time2win(&wenv->dwAttackTime     , aenv->attack_length)
       && whap_time2win(&wenv->dwFadeTime        , aenv->fade_length)
       && whap_level2win(&wenv->dwAttackLevel    , aenv->attack_level)
@@ -508,9 +508,9 @@ static bool whap_envelope2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
 }
 
 /* Converts a constant effect to directinput API. */
-static bool whap_constant2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
-                               ALLEGRO_HAPTIC_EFFECT * effect) 
-{ 
+static bool whap_constant2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
+                               ALLEGRO_HAPTIC_EFFECT * effect)
+{
    weff->effect.cbTypeSpecificParams  = sizeof(weff->parameter.constant);
    weff->effect.lpvTypeSpecificParams = &weff->parameter.constant;
    return whap_envelope2win(weff, &effect->data.constant.envelope)
@@ -519,27 +519,27 @@ static bool whap_constant2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
 
 
 /* Converts a ramp effect to directinput input API. */
-static bool whap_ramp2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+static bool whap_ramp2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
                                ALLEGRO_HAPTIC_EFFECT * effect)
 {
   weff->effect.cbTypeSpecificParams  = sizeof(weff->parameter.ramp);
   weff->effect.lpvTypeSpecificParams = &weff->parameter.ramp;
-      
+
   return whap_envelope2win(weff, &effect->data.ramp.envelope)
       && whap_slevel2win(&weff->parameter.ramp.lStart, effect->data.ramp.start_level)
       && whap_slevel2win(&weff->parameter.ramp.lEnd  , effect->data.ramp.end_level);
 }
 
 /* Converts a condition effect to directinput input API. */
-static bool whap_condition2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+static bool whap_condition2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
                                ALLEGRO_HAPTIC_EFFECT * effect)
 {
   weff->effect.cbTypeSpecificParams  = sizeof(weff->parameter.condition);
   weff->effect.lpvTypeSpecificParams = &weff->parameter.condition;
   /* XXX: no envelope here ???  */
-      
+
   return whap_level2win(&weff->parameter.condition.dwNegativeSaturation, effect->data.condition.left_saturation)
-      && whap_level2win(&weff->parameter.condition.dwPositiveSaturation, effect->data.condition.right_saturation)      
+      && whap_level2win(&weff->parameter.condition.dwPositiveSaturation, effect->data.condition.right_saturation)
       && whap_slevel2win(&weff->parameter.condition.lNegativeCoefficient, effect->data.condition.left_coeff)
       && whap_slevel2win(&weff->parameter.condition.lPositiveCoefficient, effect->data.condition.right_coeff)
       && whap_slevel2win(&weff->parameter.condition.lDeadBand          , effect->data.condition.deadband)
@@ -547,7 +547,7 @@ static bool whap_condition2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
 }
 
 /* Converts a custom effect to directinput input API. */
-static bool whap_custom2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+static bool whap_custom2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
                             ALLEGRO_HAPTIC_EFFECT * effect) {
   int index;
   weff->effect.cbTypeSpecificParams  = sizeof(weff->parameter.custom);
@@ -560,59 +560,59 @@ static bool whap_custom2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
   /* Gotta copy this to long values, and scale them too... */
   for(index =0; index < effect->data.periodic.custom_len; index ++) {
     weff->parameter.custom.rglForceData[index] =
-    (LONG)(effect->data.periodic.custom_data[index] * ((double)(1<<31)));    
+    (LONG)(effect->data.periodic.custom_data[index] * ((double)(1<<31)));
   }
   return true;
 }
 
 
 /* Converts a periodic effect to directinput input API. */
-static bool whap_periodic2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+static bool whap_periodic2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
                                ALLEGRO_HAPTIC_EFFECT * effect)
-{  
+{
   if (effect->data.periodic.waveform == ALLEGRO_HAPTIC_CUSTOM) {
     return whap_custom2win(weff, effect);
   }
-  
+
   weff->effect.cbTypeSpecificParams  = sizeof(weff->parameter.periodic);
   weff->effect.lpvTypeSpecificParams = &weff->parameter.periodic;
-      
+
   return whap_envelope2win(weff, &effect->data.periodic.envelope)
       && whap_level2win(&weff->parameter.periodic.dwMagnitude, effect->data.periodic.magnitude)
-      && whap_phase2win(&weff->parameter.periodic.dwPhase    , effect->data.periodic.phase)    
-      && whap_time2win(&weff->parameter.periodic.dwPeriod    , effect->data.periodic.period)    
+      && whap_phase2win(&weff->parameter.periodic.dwPhase    , effect->data.periodic.phase)
+      && whap_time2win(&weff->parameter.periodic.dwPeriod    , effect->data.periodic.period)
       && whap_slevel2win(&weff->parameter.periodic.lOffset   , effect->data.periodic.offset);
 }
 
 
 /* Converts a periodic effect to directinput input API. */
-static bool whap_rumble2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
+static bool whap_rumble2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
                                ALLEGRO_HAPTIC_EFFECT * effect)
-{  
+{
   weff->effect.cbTypeSpecificParams  = sizeof(weff->parameter.periodic);
   weff->effect.lpvTypeSpecificParams = &weff->parameter.periodic;
   /** Set up a simple sine wave effect for a rumble. */
-  weff->guid                        = &GUID_Sine;        
+  weff->guid                        = &GUID_Sine;
   return whap_level2win(&weff->parameter.periodic.dwMagnitude, effect->data.rumble.strong_magnitude)
-      && whap_phase2win(&weff->parameter.periodic.dwPhase    , 0)    
-      && whap_time2win(&weff->parameter.periodic.dwPeriod    , 250000)    
+      && whap_phase2win(&weff->parameter.periodic.dwPhase    , 0)
+      && whap_time2win(&weff->parameter.periodic.dwPeriod    , 250000)
       && whap_slevel2win(&weff->parameter.periodic.lOffset   , 0);
 }
 
 /* Converts Allegro haptic effect to dinput API. */
-static bool whap_effect2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff, 
-                            ALLEGRO_HAPTIC_EFFECT *effect, 
+static bool whap_effect2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
+                            ALLEGRO_HAPTIC_EFFECT *effect,
                             ALLEGRO_HAPTIC_WINDOWS *whap)
 {
-   /* Generic setup */ 
+   /* Generic setup */
    memset((void *) weff, 0, sizeof(*weff));
    /* Set global stuff. */
    weff->effect.dwSize           = sizeof(DIEFFECT);
-   weff->effect.dwGain           = DI_FFNOMINALMAX;       
+   weff->effect.dwGain           = DI_FFNOMINALMAX;
    weff->effect.dwSamplePeriod   = 0;
    weff->effect.dwFlags          = DIEFF_OBJECTOFFSETS;
    weff->effect.lpEnvelope       = NULL;
-  
+
    if (!whap_type2win(weff, effect)) {
       return false;
    }
@@ -620,12 +620,12 @@ static bool whap_effect2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
    if (!whap_direction2win(weff, effect, whap)) {
       return false;
    }
-   
+
    if (!whap_replay2win(weff, effect)) {
       return false;
    }
-   
-   
+
+
    switch (effect->type) {
      case ALLEGRO_HAPTIC_RUMBLE:
        return whap_rumble2win(weff, effect);
@@ -640,7 +640,7 @@ static bool whap_effect2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
      case ALLEGRO_HAPTIC_DAMPER:
      case ALLEGRO_HAPTIC_INERTIA:
        return whap_condition2win(weff, effect);
-     default: 
+     default:
        return false;
    }
 }
@@ -656,16 +656,18 @@ static bool whap_is_dinput_device_haptic(LPDIRECTINPUTDEVICE2 device) {
    HRESULT ret;
    DIDEVCAPS dicaps;
    /* Get capabilities. */
+   ALLEGRO_DEBUG("IDirectInputDevice8_GetCapabilities on %p\n", device);
    memset((void *) &dicaps, 0, sizeof(dicaps));
    dicaps.dwSize = sizeof (dicaps);
    ret = IDirectInputDevice8_GetCapabilities(device, &dicaps);
    if (FAILED(ret)) {
-      ALLEGRO_WARN("IDirectInputDevice8_GetCapabilities failed on %p\n", device);
+      ALLEGRO_ERROR("IDirectInputDevice8_GetCapabilities failed on %p\n", device);
       return false;
    }
-   
    /** Is it a haptic device? */
-   return ((dicaps.dwFlags & DIDC_FORCEFEEDBACK) == DIDC_FORCEFEEDBACK);
+   bool ishaptic = (dicaps.dwFlags & DIDC_FORCEFEEDBACK) == DIDC_FORCEFEEDBACK;
+   ALLEGRO_DEBUG("dicaps.dwFlags: %lu, %d, %d\n", dicaps.dwFlags, DIDC_FORCEFEEDBACK, ishaptic);
+   return (ishaptic);
 }
 
 
@@ -685,6 +687,7 @@ static bool whap_is_joystick_haptic(ALLEGRO_JOYSTICK *joy)
       return false;
    if (!al_get_joystick_active(joy))
       return false;
+   ALLEGRO_DEBUG("Checking capabilities of joystick %s\n", joydx->name);
    return whap_is_dinput_device_haptic(joydx->device);
 }
 
@@ -719,19 +722,19 @@ static ALLEGRO_HAPTIC *whap_get_from_mouse(ALLEGRO_MOUSE *mouse)
 
 /* Sets the force feedback gain on a directinput device.
  Returns true on success and false on failure.  */
-static bool whap_set_dinput_device_gain(LPDIRECTINPUTDEVICE2 device, 
+static bool whap_set_dinput_device_gain(LPDIRECTINPUTDEVICE2 device,
                                         double gain) {
    HRESULT ret;
    DIPROPDWORD dipdw;
    if (gain < 0.0) return false;
    if (gain > 1.0) return false;
-   
+
    dipdw.diph.dwSize = sizeof(DIPROPDWORD);
    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
    dipdw.diph.dwObj = 0;
    dipdw.diph.dwHow = DIPH_DEVICE;
-   dipdw.dwData     = gain * DI_FFNOMINALMAX; 
-   
+   dipdw.dwData     = gain * DI_FFNOMINALMAX;
+
    ret = IDirectInputDevice8_SetProperty(device,
                                           DIPROP_FFGAIN, &dipdw.diph);
    return (!FAILED(ret));
@@ -739,20 +742,20 @@ static bool whap_set_dinput_device_gain(LPDIRECTINPUTDEVICE2 device,
 
 /* Sets the force feedback autocentering intensity on a directinput device.
  Returns true on success and false on failure. */
-static bool whap_set_dinput_device_autocenter(LPDIRECTINPUTDEVICE2 device, 
+static bool whap_set_dinput_device_autocenter(LPDIRECTINPUTDEVICE2 device,
                                               double intensity) {
    HRESULT ret;
    DIPROPDWORD dipdw;
    if (intensity < 0.0) return false;
    if (intensity > 1.0) return false;
-   
+
    dipdw.diph.dwSize = sizeof(DIPROPDWORD);
    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
    dipdw.diph.dwObj = 0;
    dipdw.diph.dwHow = DIPH_DEVICE;
-   if (intensity < 0.5) { 
+   if (intensity < 0.5) {
       dipdw.dwData = DIPROPAUTOCENTER_OFF;
-   } else {     
+   } else {
       dipdw.dwData = DIPROPAUTOCENTER_ON;
    }
    /* Try to set the autocenter. */
@@ -768,7 +771,7 @@ whap_check_effect_callback(LPCDIEFFECTINFO info, LPVOID data)
 {
    ALLEGRO_HAPTIC * haptic = (ALLEGRO_HAPTIC *) data;
    ALLEGRO_HAPTIC_WINDOWS * whap = whap_from_al(haptic);
-   
+
    const CAP_MAP * map;
    for (map = cap_map; map->allegro_bit != -1; map++) {
       if(GUID_EQUAL(info->guid, map->guid)) {
@@ -786,13 +789,13 @@ whap_check_axes_calback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID data)
 {
    ALLEGRO_HAPTIC * haptic = (ALLEGRO_HAPTIC *) data;
    ALLEGRO_HAPTIC_WINDOWS * whap = whap_from_al(haptic);
-   
+
    whap->naxes = 0;
    if ((dev->dwType & DIDFT_AXIS) && (dev->dwFlags & DIDOI_FFACTUATOR)) {
 
         whap->axes[whap->naxes] = dev->dwOfs;
         whap->naxes++;
-        
+
         /* Stop if the axes limit is reached */
         if (whap->naxes >= HAPTICS_AXES_MAX) {
             return DIENUM_STOP;
@@ -807,7 +810,7 @@ whap_check_axes_calback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID data)
 static bool whap_initialize_dinput(ALLEGRO_HAPTIC_WINDOWS * whap) {
    HRESULT ret;
    ALLEGRO_HAPTIC * haptic = &whap->parent;
-   
+
    /* Get number of axes. */
    ret = IDirectInputDevice8_EnumObjects(whap->device,
                                           whap_check_axes_calback,
@@ -841,30 +844,30 @@ static bool whap_initialize_dinput(ALLEGRO_HAPTIC_WINDOWS * whap) {
      if (FAILED(ret)) {
       ALLEGRO_WARN("Could not get haptic device supported effects ");
       return false;
-     }  
-    
+     }
+
     /** Check if any periodic effects are supported. */
     bool periodic_ok = al_is_haptic_capable(haptic, ALLEGRO_HAPTIC_SINE);
     periodic_ok |= al_is_haptic_capable(haptic, ALLEGRO_HAPTIC_SQUARE);
     periodic_ok |= al_is_haptic_capable(haptic, ALLEGRO_HAPTIC_TRIANGLE);
     periodic_ok |= al_is_haptic_capable(haptic, ALLEGRO_HAPTIC_SAW_DOWN);
     periodic_ok |= al_is_haptic_capable(haptic, ALLEGRO_HAPTIC_SAW_UP);
- 
-    if (periodic_ok) { 
-      /* If we have any of the effects above, we can use 
+
+    if (periodic_ok) {
+      /* If we have any of the effects above, we can use
          periodic and rumble effects. */
-      whap->flags |= (ALLEGRO_HAPTIC_PERIODIC | ALLEGRO_HAPTIC_RUMBLE); 
-    }  
-   
-    if (whap_set_dinput_device_gain(whap->device, 1.0)) { 
+      whap->flags |= (ALLEGRO_HAPTIC_PERIODIC | ALLEGRO_HAPTIC_RUMBLE);
+    }
+
+    if (whap_set_dinput_device_gain(whap->device, 1.0)) {
       whap->flags |= ALLEGRO_HAPTIC_GAIN;
     }
-    
+
     /* Check autocenter and turn it off in one go. */
-    if (whap_set_dinput_device_autocenter(whap->device, 0.0)) { 
+    if (whap_set_dinput_device_autocenter(whap->device, 0.0)) {
       whap->flags |= ALLEGRO_HAPTIC_AUTOCENTER;
     }
- 
+
     return true;
 }
 
@@ -875,7 +878,7 @@ static ALLEGRO_HAPTIC *whap_get_from_joystick(ALLEGRO_JOYSTICK *joy)
 {
    ALLEGRO_JOYSTICK_DIRECTX * joydx = (ALLEGRO_JOYSTICK_DIRECTX *) joy;
    ALLEGRO_HAPTIC_WINDOWS * whap;
-   
+
    int i;
 
    if (!al_is_joystick_haptic(joy))
@@ -901,16 +904,16 @@ static ALLEGRO_HAPTIC *whap_get_from_joystick(ALLEGRO_JOYSTICK *joy)
    }
    whap->parent.gain = 1.0;
    whap->parent.autocenter = 0.0;
-   
+
    /* result is ok if init functions returns true. */
    if (!whap_initialize_dinput(whap)) {
-      al_release_haptic(&whap->parent); 
+      al_release_haptic(&whap->parent);
       al_unlock_mutex(haptic_mutex);
-      return NULL;  
+      return NULL;
    }
 
    al_unlock_mutex(haptic_mutex);
-   
+
    return &whap->parent;
 }
 
@@ -959,12 +962,12 @@ static bool whap_set_gain(ALLEGRO_HAPTIC *dev, double gain)
      whap->parent.gain = gain;
    } else {
      whap->parent.gain = 1.0;
-   } 
+   }
    return ok;
 }
 
 
-double whap_get_autocenter(ALLEGRO_HAPTIC * dev) 
+double whap_get_autocenter(ALLEGRO_HAPTIC * dev)
 {
    ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(dev);
    /* Return the stored autocenter value. It's easiest like that. */
@@ -980,7 +983,7 @@ static bool whap_set_autocenter(ALLEGRO_HAPTIC *dev, double intensity)
      whap->parent.autocenter = intensity;
    } else {
      whap->parent.autocenter = 0.0;
-   } 
+   }
    return ok;
 }
 
@@ -989,7 +992,7 @@ static int whap_get_num_effects(ALLEGRO_HAPTIC *dev)
    ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(dev);
    int n_effects;
    (void) n_effects, (void)whap;
-   
+
    return HAPTICS_EFFECTS_MAX;
 }
 
@@ -1001,7 +1004,7 @@ static bool whap_is_effect_ok(ALLEGRO_HAPTIC *haptic,
 
    caps = al_get_haptic_capabilities(haptic);
    if (caps & effect->type) {
-      return true; 
+      return true;
    }
    /* XXX: should do more checking here? */
    return false;
@@ -1015,17 +1018,17 @@ static double whap_effect_duration(ALLEGRO_HAPTIC_EFFECT *effect)
 }
 */
 static bool whap_upload_effect_helper
-  (ALLEGRO_HAPTIC_WINDOWS         * whap, 
+  (ALLEGRO_HAPTIC_WINDOWS         * whap,
    ALLEGRO_HAPTIC_EFFECT_WINDOWS  * weff,
    ALLEGRO_HAPTIC_EFFECT          * effect
-  ) 
+  )
 {
    HRESULT ret;
-   if (!whap_effect2win(weff, effect, whap)) { 
-      ALLEGRO_WARN("Could not convert haptic effect.");      
+   if (!whap_effect2win(weff, effect, whap)) {
+      ALLEGRO_WARN("Could not convert haptic effect.");
       return false;
    }
-    
+
    /* Create the effect. */
    ret = IDirectInputDevice8_CreateEffect(whap->device, (*weff->guid),
                                           &weff->effect,
@@ -1033,15 +1036,15 @@ static bool whap_upload_effect_helper
    if (FAILED(ret)) {
       ALLEGRO_WARN("Could not create haptic effect.");
       return false;
-   } 
-  
+   }
+
      /* Upload the effect to the device. */
-   ret = IDirectInputEffect_Download(weff->ref);   
+   ret = IDirectInputEffect_Download(weff->ref);
    if (FAILED(ret)) {
       ALLEGRO_WARN("Could not upload haptic effect.");
       return false;
-   }   
-   
+   }
+
    return true;
 }
 
@@ -1051,36 +1054,36 @@ static bool whap_upload_effect(ALLEGRO_HAPTIC *dev,
    bool ok;
    ALLEGRO_HAPTIC_WINDOWS *whap = whap_from_al(dev);
    ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff = NULL;
-   
+
    ASSERT(dev);
    ASSERT(id);
    ASSERT(effect);
-   
+
    /* Set id's values to indicate failure beforehand. */
    id->_haptic          = NULL;
    id->_id              = -1;
    id->_pointer         = NULL;
    id->_playing         = false;
    id->_effect_duration = 0.0;
-   
+
    al_lock_mutex(haptic_mutex);
-   
+
    /* Look for a free haptic effect slot. */
    weff = whap_get_available_effect(whap);
    /* No more space for an effect. */
    if (!weff) {
       ALLEGRO_WARN("No free effect slot.");
       return false;
-   }  
+   }
    ok = whap_upload_effect_helper(whap, weff, effect);
    if (ok) {
       /* set ID handle to signify success */
       id->_haptic  = dev;
       id->_pointer = weff;
-      id->_id      = weff->id;      
+      id->_id      = weff->id;
       id->_effect_duration = al_get_haptic_effect_duration(effect);
-   } 
-   
+   }
+
    al_unlock_mutex(haptic_mutex);
    return ok;
 }
@@ -1090,47 +1093,47 @@ static bool whap_play_effect(ALLEGRO_HAPTIC_EFFECT_ID *id, int loops)
 {
    HRESULT res;
    ALLEGRO_HAPTIC_WINDOWS *whap = (ALLEGRO_HAPTIC_WINDOWS *) id->_haptic;
-   ALLEGRO_HAPTIC_EFFECT_WINDOWS *weff; 
+   ALLEGRO_HAPTIC_EFFECT_WINDOWS *weff;
    if (!whap)
       return false;
    weff = whap->effects + id->_id;
-   
-   res = IDirectInputEffect_Start(weff->ref, loops, 0);  
+
+   res = IDirectInputEffect_Start(weff->ref, loops, 0);
    if(FAILED(res)) {
-      ALLEGRO_WARN("Failed to play an effect."); 
+      ALLEGRO_WARN("Failed to play an effect.");
       return false;
    }
    id->_playing = true;
    // id->_
-   
+
    return true;
-   
-   
-   
 
-   
-   
+
+
+
+
+
 }
 
 
 static bool whap_stop_effect(ALLEGRO_HAPTIC_EFFECT_ID *id)
-{   
+{
    HRESULT res;
    ALLEGRO_HAPTIC_WINDOWS *whap = (ALLEGRO_HAPTIC_WINDOWS *) id->_haptic;
-   ALLEGRO_HAPTIC_EFFECT_WINDOWS *weff; 
-   
+   ALLEGRO_HAPTIC_EFFECT_WINDOWS *weff;
+
    if (!whap)
       return false;
    weff = whap->effects + id->_id;
-   
-   res = IDirectInputEffect_Stop(weff->ref);  
+
+   res = IDirectInputEffect_Stop(weff->ref);
    if(FAILED(res)) {
-      ALLEGRO_WARN("Failed to play an effect."); 
+      ALLEGRO_WARN("Failed to play an effect.");
       return false;
    }
    id->_playing = false;
-   
-   
+
+
    return true;
 }
 
@@ -1141,15 +1144,15 @@ static bool whap_is_effect_playing(ALLEGRO_HAPTIC_EFFECT_ID *id)
    HRESULT res;
    DWORD flags;
    ALLEGRO_HAPTIC_WINDOWS *whap = (ALLEGRO_HAPTIC_WINDOWS *) id->_haptic;
-   ALLEGRO_HAPTIC_EFFECT_WINDOWS *weff; 
-   
+   ALLEGRO_HAPTIC_EFFECT_WINDOWS *weff;
+
    if (!whap)
       return false;
    weff = whap->effects + id->_id;
-   
-   res = IDirectInputEffect_GetEffectStatus(weff->ref, &flags);  
+
+   res = IDirectInputEffect_GetEffectStatus(weff->ref, &flags);
    if(FAILED(res)) {
-      ALLEGRO_WARN("Failed to get the status of effect."); 
+      ALLEGRO_WARN("Failed to get the status of effect.");
       return false;
    }
    return ((flags & DIEGES_PLAYING) == DIEGES_PLAYING);
@@ -1161,10 +1164,10 @@ static bool whap_release_effect(ALLEGRO_HAPTIC_EFFECT_ID *id)
 {
    ALLEGRO_HAPTIC_WINDOWS *whap = (ALLEGRO_HAPTIC_WINDOWS *)id->_haptic;
    ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff;
-   
-   
+
+
    whap_stop_effect(id);
-   
+
    weff = whap->effects + id->_id;
    return whap_release_effect_windows(weff);
 }
@@ -1178,12 +1181,12 @@ static bool whap_release(ALLEGRO_HAPTIC *haptic)
 
    if (!whap->active)
       return false;
-   
+
    /* Release all effects for this device. */
    for (index = 0; index < HAPTICS_EFFECTS_MAX ; index ++) {
      whap_release_effect_windows(whap->effects + index);
    }
-   
+
    whap->active = false;
    whap->device = NULL;
    return true;
diff --git a/src/win/wsystem.c b/src/win/wsystem.c
index 6907108..4b088a4 100644
--- a/src/win/wsystem.c
+++ b/src/win/wsystem.c
@@ -311,7 +311,8 @@ static ALLEGRO_JOYSTICK_DRIVER *win_get_joystick_driver(void)
 
 static ALLEGRO_HAPTIC_DRIVER *win_get_haptic_driver(void)
 {
-   return _al_haptic_driver_list[0].driver;
+   return &_al_hapdrv_directx;
+   /* return _al_haptic_driver_list[0].driver; XXX: this list is empty for some reason!?  */
 }
 
 static int win_get_num_display_modes(void)
@@ -698,7 +699,7 @@ static ALLEGRO_SYSTEM_INTERFACE *_al_system_win_driver(void)
    vt->get_keyboard_driver = win_get_keyboard_driver;
    vt->get_mouse_driver = win_get_mouse_driver;
    vt->get_touch_input_driver = win_get_touch_input_driver;
-   vt->get_haptic_driver = win_get_haptic_driver;   
+   vt->get_haptic_driver = win_get_haptic_driver;
    vt->get_joystick_driver = win_get_joystick_driver;
    vt->get_num_display_modes = win_get_num_display_modes;
    vt->get_display_mode = win_get_display_mode;
-- 
1.7.10.4


From 10dee99ca74f9fd67b29e710e7a862e669eeea66 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Fri, 9 May 2014 14:20:39 +0200
Subject: [PATCH 12/15] Bugfixes based on testing on WINE.

---
 src/win/whaptic.cpp |  174 ++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 130 insertions(+), 44 deletions(-)

diff --git a/src/win/whaptic.cpp b/src/win/whaptic.cpp
index 7128516..8c31b13 100644
--- a/src/win/whaptic.cpp
+++ b/src/win/whaptic.cpp
@@ -281,7 +281,7 @@ static ALLEGRO_HAPTIC_EFFECT_WINDOWS *whap_get_available_effect(
 /* Releases a windows haptics effect and unloads it from the device. */
 static bool whap_release_effect_windows(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff) {
    bool result = true;
-   if(!weff) return false; /* make it easy to handle alll cases later on.*/
+   if(!weff) return false; /* make it easy to handle all cases later on.*/
    if(!weff->active) return false; /* already not in use, bail out. */
 
    /* Unload the effect from the device. */
@@ -462,7 +462,7 @@ static bool whap_slevel2win(LONG*res, double level)
 {
    ASSERT(res);
 
-   if (level < 1.0 || level > 1.0)
+   if (level < -1.0 || level > 1.0)
       return false;
    *res = (LONG) (level * DI_FFNOMINALMAX);
    return true;
@@ -595,7 +595,7 @@ static bool whap_rumble2win(ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff,
   weff->guid                        = &GUID_Sine;
   return whap_level2win(&weff->parameter.periodic.dwMagnitude, effect->data.rumble.strong_magnitude)
       && whap_phase2win(&weff->parameter.periodic.dwPhase    , 0)
-      && whap_time2win(&weff->parameter.periodic.dwPeriod    , 250000)
+      && whap_time2win(&weff->parameter.periodic.dwPeriod    , 0.01)
       && whap_slevel2win(&weff->parameter.periodic.lOffset   , 0);
 }
 
@@ -657,7 +657,6 @@ static bool whap_is_dinput_device_haptic(LPDIRECTINPUTDEVICE2 device) {
    DIDEVCAPS dicaps;
    /* Get capabilities. */
    ALLEGRO_DEBUG("IDirectInputDevice8_GetCapabilities on %p\n", device);
-   memset((void *) &dicaps, 0, sizeof(dicaps));
    dicaps.dwSize = sizeof (dicaps);
    ret = IDirectInputDevice8_GetCapabilities(device, &dicaps);
    if (FAILED(ret)) {
@@ -665,7 +664,7 @@ static bool whap_is_dinput_device_haptic(LPDIRECTINPUTDEVICE2 device) {
       return false;
    }
    /** Is it a haptic device? */
-   bool ishaptic = (dicaps.dwFlags & DIDC_FORCEFEEDBACK) == DIDC_FORCEFEEDBACK;
+   bool ishaptic = (dicaps.dwFlags & DIDC_FORCEFEEDBACK);
    ALLEGRO_DEBUG("dicaps.dwFlags: %lu, %d, %d\n", dicaps.dwFlags, DIDC_FORCEFEEDBACK, ishaptic);
    return (ishaptic);
 }
@@ -785,23 +784,20 @@ whap_check_effect_callback(LPCDIEFFECTINFO info, LPVOID data)
 
 /* Callback to check which axes are supported. */
 static BOOL CALLBACK
-whap_check_axes_calback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID data)
+whap_check_axes_callback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID data)
 {
    ALLEGRO_HAPTIC * haptic = (ALLEGRO_HAPTIC *) data;
    ALLEGRO_HAPTIC_WINDOWS * whap = whap_from_al(haptic);
 
-   whap->naxes = 0;
    if ((dev->dwType & DIDFT_AXIS) && (dev->dwFlags & DIDOI_FFACTUATOR)) {
+      whap->axes[whap->naxes] = dev->dwOfs;
+      whap->naxes++;
 
-        whap->axes[whap->naxes] = dev->dwOfs;
-        whap->naxes++;
-
-        /* Stop if the axes limit is reached */
-        if (whap->naxes >= HAPTICS_AXES_MAX) {
-            return DIENUM_STOP;
-        }
+      /* Stop if the axes limit is reached */
+      if (whap->naxes >= HAPTICS_AXES_MAX) {
+         return DIENUM_STOP;
+      }
     }
-
     return DIENUM_CONTINUE;
 }
 
@@ -810,13 +806,14 @@ whap_check_axes_calback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID data)
 static bool whap_initialize_dinput(ALLEGRO_HAPTIC_WINDOWS * whap) {
    HRESULT ret;
    ALLEGRO_HAPTIC * haptic = &whap->parent;
-
-   /* Get number of axes. */
+   /* Set number of axes to zero, and then ... */
+   whap->naxes = 0;
+   /* ... get number of axes. */
    ret = IDirectInputDevice8_EnumObjects(whap->device,
-                                          whap_check_axes_calback,
+                                          whap_check_axes_callback,
                                           haptic, DIDFT_AXIS);
    if (FAILED(ret)) {
-      ALLEGRO_WARN("Could not get haptic device axes ");
+      ALLEGRO_WARN("Could not get haptic device axes \n");
       return false;
    }
 
@@ -826,14 +823,14 @@ static bool whap_initialize_dinput(ALLEGRO_HAPTIC_WINDOWS * whap) {
     ret = IDirectInputDevice8_SendForceFeedbackCommand(whap->device,
                                                        DISFFC_RESET);
     if (FAILED(ret)) {
-        ALLEGRO_WARN("Could not reset haptic device");
+        ALLEGRO_WARN("Could not reset haptic device \n");
     }
 
     /* Enable all actuators. */
     ret = IDirectInputDevice8_SendForceFeedbackCommand(whap->device,
                                                        DISFFC_SETACTUATORSON);
     if (FAILED(ret)) {
-      ALLEGRO_WARN("Could not enable haptic device actuators ");
+      ALLEGRO_WARN("Could not enable haptic device actuators\n");
       return false;
     }
 
@@ -842,7 +839,7 @@ static bool whap_initialize_dinput(ALLEGRO_HAPTIC_WINDOWS * whap) {
                                           whap_check_effect_callback, haptic,
                                           DIEFT_ALL);
      if (FAILED(ret)) {
-      ALLEGRO_WARN("Could not get haptic device supported effects ");
+      ALLEGRO_WARN("Could not get haptic device supported effects\n");
       return false;
      }
 
@@ -1011,6 +1008,75 @@ static bool whap_is_effect_ok(ALLEGRO_HAPTIC *haptic,
 }
 
 
+struct dinput_error_pair {
+  HRESULT error;
+  const char * text;
+};
+
+#define DIMKEP(ERROR) {ERROR, #ERROR}
+
+struct dinput_error_pair dinput_errors[] = {
+  DIMKEP(DI_BUFFEROVERFLOW),
+  DIMKEP(DI_DOWNLOADSKIPPED),
+  DIMKEP(DI_EFFECTRESTARTED),
+  DIMKEP(DI_NOEFFECT),
+  DIMKEP(DI_NOTATTACHED),
+  DIMKEP(DI_OK),
+  DIMKEP(DI_POLLEDDEVICE),
+  DIMKEP(DI_PROPNOEFFECT),
+  DIMKEP(DI_SETTINGSNOTSAVED),
+  DIMKEP(DI_TRUNCATED),
+  DIMKEP(DI_TRUNCATEDANDRESTARTED),
+  DIMKEP(DI_WRITEPROTECT),
+  DIMKEP(DIERR_ACQUIRED),
+  DIMKEP(DIERR_ALREADYINITIALIZED),
+  DIMKEP(DIERR_BADDRIVERVER),
+  DIMKEP(DIERR_BETADIRECTINPUTVERSION),
+  DIMKEP(DIERR_DEVICEFULL),
+  DIMKEP(DIERR_DEVICENOTREG),
+  DIMKEP(DIERR_EFFECTPLAYING),
+  DIMKEP(DIERR_GENERIC),
+  DIMKEP(DIERR_HANDLEEXISTS),
+  DIMKEP(DIERR_HASEFFECTS),
+  DIMKEP(DIERR_INCOMPLETEEFFECT),
+  DIMKEP(DIERR_INPUTLOST),
+  DIMKEP(DIERR_INVALIDPARAM),
+  DIMKEP(DIERR_MAPFILEFAIL),
+  DIMKEP(DIERR_MOREDATA),
+  DIMKEP(DIERR_NOAGGREGATION),
+  DIMKEP(DIERR_NOINTERFACE),
+  DIMKEP(DIERR_NOTACQUIRED),
+  DIMKEP(DIERR_NOTBUFFERED),
+  DIMKEP(DIERR_NOTDOWNLOADED),
+  DIMKEP(DIERR_NOTEXCLUSIVEACQUIRED),
+  DIMKEP(DIERR_NOTFOUND),
+  DIMKEP(DIERR_NOTINITIALIZED),
+  DIMKEP(DIERR_OBJECTNOTFOUND),
+  DIMKEP(DIERR_OLDDIRECTINPUTVERSION),
+  DIMKEP(DIERR_OTHERAPPHASPRIO),
+  DIMKEP(DIERR_OUTOFMEMORY),
+  DIMKEP(DIERR_READONLY),
+  DIMKEP(DIERR_REPORTFULL),
+  DIMKEP(DIERR_UNPLUGGED),
+  DIMKEP(DIERR_UNSUPPORTED),
+  DIMKEP(E_HANDLE),
+  DIMKEP(E_PENDING),
+  DIMKEP(E_POINTER),
+  { 0, NULL } 
+};
+
+static void warn_on_error(HRESULT hr) {
+  struct dinput_error_pair * pair = dinput_errors;
+  while (pair->text) {
+    if (hr == pair->error) {
+      ALLEGRO_WARN("HRESULT error: %s\n", pair->text);
+    }
+    pair++;
+  }
+  ALLEGRO_WARN("Unknown HRESULT error: %u\n", (unsigned int)hr);
+}
+
+
 /*
 static double whap_effect_duration(ALLEGRO_HAPTIC_EFFECT *effect)
 {
@@ -1025,7 +1091,7 @@ static bool whap_upload_effect_helper
 {
    HRESULT ret;
    if (!whap_effect2win(weff, effect, whap)) {
-      ALLEGRO_WARN("Could not convert haptic effect.");
+      ALLEGRO_WARN("Could not convert haptic effect.\n");
       return false;
    }
 
@@ -1034,14 +1100,16 @@ static bool whap_upload_effect_helper
                                           &weff->effect,
                                           &weff->ref   , NULL);
    if (FAILED(ret)) {
-      ALLEGRO_WARN("Could not create haptic effect.");
+      ALLEGRO_WARN("Could not create haptic effect.\n");
+      warn_on_error(ret);
       return false;
    }
 
      /* Upload the effect to the device. */
    ret = IDirectInputEffect_Download(weff->ref);
    if (FAILED(ret)) {
-      ALLEGRO_WARN("Could not upload haptic effect.");
+      ALLEGRO_WARN("Could not upload haptic effect.\n");
+      warn_on_error(ret);
       return false;
    }
 
@@ -1065,6 +1133,8 @@ static bool whap_upload_effect(ALLEGRO_HAPTIC *dev,
    id->_pointer         = NULL;
    id->_playing         = false;
    id->_effect_duration = 0.0;
+   id->_start_time      = 0.0;
+   id->_end_time        = 0.0;
 
    al_lock_mutex(haptic_mutex);
 
@@ -1081,7 +1151,7 @@ static bool whap_upload_effect(ALLEGRO_HAPTIC *dev,
       id->_haptic  = dev;
       id->_pointer = weff;
       id->_id      = weff->id;
-      id->_effect_duration = al_get_haptic_effect_duration(effect);
+      id->_effect_duration = al_get_haptic_effect_duration(effect);      
    }
 
    al_unlock_mutex(haptic_mutex);
@@ -1094,25 +1164,23 @@ static bool whap_play_effect(ALLEGRO_HAPTIC_EFFECT_ID *id, int loops)
    HRESULT res;
    ALLEGRO_HAPTIC_WINDOWS *whap = (ALLEGRO_HAPTIC_WINDOWS *) id->_haptic;
    ALLEGRO_HAPTIC_EFFECT_WINDOWS *weff;
-   if (!whap)
+   if ((!whap) || (id->_id < 0))
       return false;
+   
    weff = whap->effects + id->_id;
 
+  /* IDirectInputEffect_SetParameters(weff->ref, weff->effect, weff-> flags); */
+  
    res = IDirectInputEffect_Start(weff->ref, loops, 0);
    if(FAILED(res)) {
       ALLEGRO_WARN("Failed to play an effect.");
       return false;
    }
    id->_playing = true;
-   // id->_
-
+   id->_start_time = al_get_time();
+   id->_end_time   = id->_start_time;
+   id->_end_time  += id->_effect_duration * (double)loops;
    return true;
-
-
-
-
-
-
 }
 
 
@@ -1122,8 +1190,9 @@ static bool whap_stop_effect(ALLEGRO_HAPTIC_EFFECT_ID *id)
    ALLEGRO_HAPTIC_WINDOWS *whap = (ALLEGRO_HAPTIC_WINDOWS *) id->_haptic;
    ALLEGRO_HAPTIC_EFFECT_WINDOWS *weff;
 
-   if (!whap)
+   if ((!whap) || (id->_id < 0))
       return false;
+   
    weff = whap->effects + id->_id;
 
    res = IDirectInputEffect_Stop(weff->ref);
@@ -1142,20 +1211,36 @@ static bool whap_is_effect_playing(ALLEGRO_HAPTIC_EFFECT_ID *id)
 {
    ASSERT(id);
    HRESULT res;
-   DWORD flags;
+   DWORD flags = 0;
    ALLEGRO_HAPTIC_WINDOWS *whap = (ALLEGRO_HAPTIC_WINDOWS *) id->_haptic;
    ALLEGRO_HAPTIC_EFFECT_WINDOWS *weff;
 
-   if (!whap)
+   if ((!whap) || (id->_id < 0) || (!id->_playing))
       return false;
-   weff = whap->effects + id->_id;
 
+   weff = whap->effects + id->_id;
+    
    res = IDirectInputEffect_GetEffectStatus(weff->ref, &flags);
    if(FAILED(res)) {
       ALLEGRO_WARN("Failed to get the status of effect.");
-      return false;
-   }
-   return ((flags & DIEGES_PLAYING) == DIEGES_PLAYING);
+      /* If we get here, then use the play time in stead to 
+       * see if the effect should still be playing. 
+       * Do this because in case GeteffectStatus fails, we can't
+       * assume the sample isn't playing. In fact, if the play command 
+       * was sucessful, it should still be playing as long as the play 
+       * time has not passed. 
+       */
+      return (al_get_time() < id->_end_time);
+   }   
+   if (flags & DIEGES_PLAYING) return true;
+    /* WINE is bugged here, it doesn't set flags, but it also 
+    * just returns DI_OK. Thats why here, don't believe the API 
+    * when it the playing flag isn't set if the effect's duration
+    * has not passed. On real Windows it should probably always be the 
+    * case that the effect will have played completely when 
+    * the play time has ended.
+    */
+   return (al_get_time() < id->_end_time);
 }
 
 
@@ -1164,8 +1249,9 @@ static bool whap_release_effect(ALLEGRO_HAPTIC_EFFECT_ID *id)
 {
    ALLEGRO_HAPTIC_WINDOWS *whap = (ALLEGRO_HAPTIC_WINDOWS *)id->_haptic;
    ALLEGRO_HAPTIC_EFFECT_WINDOWS * weff;
-
-
+   if ((!whap) || (id->_id < 0))
+      return false;
+   
    whap_stop_effect(id);
 
    weff = whap->effects + id->_id;
-- 
1.7.10.4


From febd73949a24e0569740f7a83c15c46ecd3d3797 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Fri, 9 May 2014 14:21:46 +0200
Subject: [PATCH 13/15] Small changes to the first haptics test program.

---
 examples/ex_haptic.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/ex_haptic.c b/examples/ex_haptic.c
index 2a93ad2..2b10f7b 100644
--- a/examples/ex_haptic.c
+++ b/examples/ex_haptic.c
@@ -45,7 +45,7 @@ static void test_haptic_joystick(ALLEGRO_JOYSTICK *joy)
       al_upload_haptic_effect(haptic, &effect, &id));
 
    log_printf("Playing effect: %d.\n",
-      al_play_haptic_effect(&id, 5));
+      al_play_haptic_effect(&id, 3));
 
    do {
       al_rest(0.1);
-- 
1.7.10.4


From 633491aca139e655623179671e8a64866544ab9d Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Fri, 9 May 2014 14:22:41 +0200
Subject: [PATCH 14/15] Automatically release effects if playing didn't work
 out in al_upload_and_play_haptic_effect. Also adjust
 the documentation to be more clear about when
 al_release_haptic_effect is needed.

---
 docs/src/refman/haptic.txt |   35 +++++++++++++++++++++++++++++++----
 src/haptic.c               |    8 +++++++-
 2 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/docs/src/refman/haptic.txt b/docs/src/refman/haptic.txt
index 2c1edd5..1550184 100644
--- a/docs/src/refman/haptic.txt
+++ b/docs/src/refman/haptic.txt
@@ -345,7 +345,9 @@ Since: 5.1.8
 
 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.
+a joystick is hot plugged. This function also automatically releases all 
+haptic effects that are still uploaded to the device and that have not been
+released manually using [al_release_haptic_effect].
 
 Since: 5.1.8
 
@@ -434,7 +436,8 @@ Since: 5.1.9
 ## 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.
+device.  This depends on the operating system, driver, platform and the 
+device itself. 
 
 Since: 5.1.8
 
@@ -451,11 +454,15 @@ 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].  This `id` can be used to control
 playback of the effect.  Returns true if the effect was successfully uploaded,
-false if not.
+false if not. 
+
+The function [al_get_num_haptic_effects] returns how many effects can 
+be uploaded to the device at the same time.
 
 The same haptic effect can be uploaded several times, as long as care is
 taken to pass in a different [ALLEGRO_HAPTIC_EFFECT_ID].
 
+
 Since: 5.1.8
 
 ## API: al_play_haptic_effect
@@ -476,7 +483,14 @@ Since: 5.1.8
 ## 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 successful, false if either failed.
+true if the upload and playback were successful, false if either failed. 
+
+In case false is returned, the haptic effect will be automatically 
+released as if [al_release_haptic_effect] had been called, so there 
+is no need to call it again manually in this case. However, 
+if true is returned, it is necessary to call [al_release_haptic_effect]
+when the effect isn't needed anymore, to prevent the amount of available 
+effects on the haptic devicefrom running out.
 
 Since: 5.1.8
 
@@ -515,6 +529,12 @@ 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].
 
+This function is called automatically when you call [al_release_haptic] 
+on a [ALLEGRO_HAPTIC] for all effects that are still uploaded to the device.
+Therefore this function is most useful if you want to upload and release
+haptic effects dynamically, for example as a way to circumvent the limit 
+imposed by [al_get_num_haptic_effects].
+
 Since: 5.1.8
 
 ## API: al_rumble_haptic
@@ -529,4 +549,11 @@ You must also pass in a pointer to a user allocated
 control playback of the effect.  Returns true if the rumble effect was
 successfully uploaded and started, false if not.
 
+In case false is returned, the rumble effect will be automatically 
+released as if [al_release_haptic_effect] had been called, so there 
+is no need to call it again manually in this case. However, 
+if true is returned, it is necessary to call [al_release_haptic_effect]
+when the effect isn't needed anymore, to prevent the amount of available 
+effects on the haptic device from running out.
+
 Since: 5.1.8
diff --git a/src/haptic.c b/src/haptic.c
index 739bbe8..c89c261 100644
--- a/src/haptic.c
+++ b/src/haptic.c
@@ -306,7 +306,13 @@ bool al_upload_and_play_haptic_effect(ALLEGRO_HAPTIC *hap,
 
    if (!al_upload_haptic_effect(hap, effect, id))
       return false;
-   return al_play_haptic_effect(id, loop);
+   /* If playing the effect failed, unload the haptic effect automatically 
+    */
+   if (!al_play_haptic_effect(id, loop)) {
+     al_release_haptic_effect(id);
+     return false;
+   }
+   return true;
 }
 
 
-- 
1.7.10.4


From 61528504be81c15dcc85fd859171ac0c66ed3868 Mon Sep 17 00:00:00 2001
From: Beoran <beoran@xxxxxxxxx>
Date: Fri, 9 May 2014 14:29:46 +0200
Subject: [PATCH 15/15] Make note of the platforms that are dupported or not
 in the documentation.

---
 docs/src/refman/haptic.txt |    5 +++++
 1 file changed, 5 insertions(+)

diff --git a/docs/src/refman/haptic.txt b/docs/src/refman/haptic.txt
index 1550184..2bdf34a 100644
--- a/docs/src/refman/haptic.txt
+++ b/docs/src/refman/haptic.txt
@@ -7,6 +7,11 @@ These functions are declared in the main Allegro header file:
  #include <allegro5/allegro.h>
 ~~~~
 
+Currently force feedback is fully supported on Linux and on Windows for 
+DirectInput compatible devices. There is also minimal support for
+Android. It is not yet supported on OSX, iOS, or on Windows for XInput 
+compatible devices.
+
 ## API: ALLEGRO_HAPTIC
 
 This is an abstract data type representing a haptic device that supports
-- 
1.7.10.4



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