- sync dinput with wine
authorKamil Hornicek <kamil.hornicek@reactos.org>
Mon, 2 Feb 2009 21:09:39 +0000 (21:09 +0000)
committerKamil Hornicek <kamil.hornicek@reactos.org>
Mon, 2 Feb 2009 21:09:39 +0000 (21:09 +0000)
svn path=/trunk/; revision=39290

15 files changed:
reactos/dll/directx/dinput/data_formats.c
reactos/dll/directx/dinput/device.c
reactos/dll/directx/dinput/device_private.h
reactos/dll/directx/dinput/dinput.rbuild
reactos/dll/directx/dinput/dinput_main.c
reactos/dll/directx/dinput/dinput_private.h
reactos/dll/directx/dinput/effect_linuxinput.c
reactos/dll/directx/dinput/joystick_linux.c
reactos/dll/directx/dinput/joystick_linuxinput.c
reactos/dll/directx/dinput/keyboard.c
reactos/dll/directx/dinput/mouse.c
reactos/dll/directx/dinput/regsvr.c
reactos/dll/directx/dinput/version.rc
reactos/include/psdk/dinput.h
reactos/media/doc/README.WINE

index 49ce8fc..1f25556 100644 (file)
@@ -13,7 +13,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #include <stdarg.h>
@@ -74,7 +74,7 @@ const DIDATAFORMAT c_dfDIJoystick = {
     sizeof(DIDATAFORMAT),
     sizeof(DIOBJECTDATAFORMAT),
     DIDF_ABSAXIS,
-    sizeof(DIJOYSTATE2),
+    sizeof(DIJOYSTATE),
     numObjects(dfDIJoystick),
     (LPDIOBJECTDATAFORMAT)dfDIJoystick
 };
index a1e4031..941206c 100644 (file)
@@ -16,7 +16,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 /* This file contains all the Device specific functions that can be used as stubs
 #include "wine/unicode.h"
 #include "windef.h"
 #include "winbase.h"
+#include "winreg.h"
+#include "winuser.h"
 #include "winerror.h"
 #include "dinput.h"
 #include "device_private.h"
+#include "dinput_private.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
 
 /******************************************************************************
  *     Various debugging tools
  */
-void _dump_cooperativelevel_DI(DWORD dwFlags) {
+static void _dump_cooperativelevel_DI(DWORD dwFlags) {
     if (TRACE_ON(dinput)) {
        unsigned int   i;
        static const struct {
@@ -52,17 +55,19 @@ void _dump_cooperativelevel_DI(DWORD dwFlags) {
            FE(DISCL_BACKGROUND),
            FE(DISCL_EXCLUSIVE),
            FE(DISCL_FOREGROUND),
-           FE(DISCL_NONEXCLUSIVE)
+           FE(DISCL_NONEXCLUSIVE),
+           FE(DISCL_NOWINKEY)
 #undef FE
        };
+       TRACE(" cooperative level : ");
        for (i = 0; i < (sizeof(flags) / sizeof(flags[0])); i++)
            if (flags[i].mask & dwFlags)
-               DPRINTF("%s ",flags[i].name);
-       DPRINTF("\n");
+               TRACE("%s ",flags[i].name);
+       TRACE("\n");
     }
 }
 
-void _dump_EnumObjects_flags(DWORD dwFlags) {
+static void _dump_EnumObjects_flags(DWORD dwFlags) {
     if (TRACE_ON(dinput)) {
        unsigned int   i;
        DWORD type, instance;
@@ -77,7 +82,7 @@ void _dump_EnumObjects_flags(DWORD dwFlags) {
            FE(DIDFT_TGLBUTTON),
            FE(DIDFT_POV),
            FE(DIDFT_COLLECTION),
-           FE(DIDFT_NODATA),
+           FE(DIDFT_NODATA),       
            FE(DIDFT_FFACTUATOR),
            FE(DIDFT_FFEFFECTTRIGGER),
            FE(DIDFT_OUTPUT),
@@ -88,51 +93,47 @@ void _dump_EnumObjects_flags(DWORD dwFlags) {
        };
        type = (dwFlags & 0xFF0000FF);
        instance = ((dwFlags >> 8) & 0xFFFF);
-       DPRINTF("Type:");
+       TRACE("Type:");
        if (type == DIDFT_ALL) {
-           DPRINTF(" DIDFT_ALL");
+           TRACE(" DIDFT_ALL");
        } else {
            for (i = 0; i < (sizeof(flags) / sizeof(flags[0])); i++) {
                if (flags[i].mask & type) {
                    type &= ~flags[i].mask;
-                   DPRINTF(" %s",flags[i].name);
+                   TRACE(" %s",flags[i].name);
                }
            }
            if (type) {
-               DPRINTF(" (unhandled: %08lx)", type);
+                TRACE(" (unhandled: %08x)", type);
            }
        }
-       DPRINTF(" / Instance: ");
+       TRACE(" / Instance: ");
        if (instance == ((DIDFT_ANYINSTANCE >> 8) & 0xFFFF)) {
-           DPRINTF("DIDFT_ANYINSTANCE");
+           TRACE("DIDFT_ANYINSTANCE");
        } else {
-           DPRINTF("%3ld", instance);
+            TRACE("%3d", instance);
        }
     }
 }
 
 void _dump_DIPROPHEADER(LPCDIPROPHEADER diph) {
     if (TRACE_ON(dinput)) {
-       DPRINTF("  - dwObj = 0x%08lx\n", diph->dwObj);
-       DPRINTF("  - dwHow = %s\n",
-               ((diph->dwHow == DIPH_DEVICE) ? "DIPH_DEVICE" :
-                ((diph->dwHow == DIPH_BYOFFSET) ? "DIPH_BYOFFSET" :
-                 ((diph->dwHow == DIPH_BYID)) ? "DIPH_BYID" : "unknown")));
+        TRACE("  - dwObj = 0x%08x\n", diph->dwObj);
+        TRACE("  - dwHow = %s\n",
+            ((diph->dwHow == DIPH_DEVICE) ? "DIPH_DEVICE" :
+            ((diph->dwHow == DIPH_BYOFFSET) ? "DIPH_BYOFFSET" :
+            ((diph->dwHow == DIPH_BYID)) ? "DIPH_BYID" : "unknown")));
     }
 }
 
-void _dump_OBJECTINSTANCEA(DIDEVICEOBJECTINSTANCEA *ddoi) {
-    if (TRACE_ON(dinput)) {
-       DPRINTF("    - enumerating : %s ('%s') - %2ld - 0x%08lx - %s\n",
-               debugstr_guid(&ddoi->guidType), _dump_dinput_GUID(&ddoi->guidType), ddoi->dwOfs, ddoi->dwType, ddoi->tszName);
-    }
+void _dump_OBJECTINSTANCEA(const DIDEVICEOBJECTINSTANCEA *ddoi) {
+    TRACE("    - enumerating : %s ('%s') - %2d - 0x%08x - %s\n",
+        debugstr_guid(&ddoi->guidType), _dump_dinput_GUID(&ddoi->guidType), ddoi->dwOfs, ddoi->dwType, ddoi->tszName);
 }
 
-void _dump_OBJECTINSTANCEW(DIDEVICEOBJECTINSTANCEW *ddoi) {
-    if (TRACE_ON(dinput)) {
-       DPRINTF("    - enumerating : %s ('%s'), - %2ld - 0x%08lx - %s\n",
-               debugstr_guid(&ddoi->guidType), _dump_dinput_GUID(&ddoi->guidType), ddoi->dwOfs, ddoi->dwType, debugstr_w(ddoi->tszName));
-    }
+void _dump_OBJECTINSTANCEW(const DIDEVICEOBJECTINSTANCEW *ddoi) {
+    TRACE("    - enumerating : %s ('%s'), - %2d - 0x%08x - %s\n",
+        debugstr_guid(&ddoi->guidType), _dump_dinput_GUID(&ddoi->guidType), ddoi->dwOfs, ddoi->dwType, debugstr_w(ddoi->tszName));
 }
 
 /* This function is a helper to convert a GUID into any possible DInput GUID out there */
@@ -178,70 +179,122 @@ const char *_dump_dinput_GUID(const GUID *guid) {
            return guids[i].name;
        }
     }
-    return "Unknown GUID";
+    return debugstr_guid(guid);
 }
 
 void _dump_DIDATAFORMAT(const DIDATAFORMAT *df) {
     unsigned int i;
 
     TRACE("Dumping DIDATAFORMAT structure:\n");
-    TRACE("  - dwSize: %ld\n", df->dwSize);
+    TRACE("  - dwSize: %d\n", df->dwSize);
     if (df->dwSize != sizeof(DIDATAFORMAT)) {
-       WARN("Non-standard DIDATAFORMAT structure size (%ld instead of %d).\n", df->dwSize, sizeof(DIDATAFORMAT));
+        WARN("Non-standard DIDATAFORMAT structure size %d\n", df->dwSize);
     }
-    TRACE("  - dwObjsize: %ld\n", df->dwObjSize);
+    TRACE("  - dwObjsize: %d\n", df->dwObjSize);
     if (df->dwObjSize != sizeof(DIOBJECTDATAFORMAT)) {
-       WARN("Non-standard DIOBJECTDATAFORMAT structure size (%ld instead of %d).\n", df->dwObjSize, sizeof(DIOBJECTDATAFORMAT));
+        WARN("Non-standard DIOBJECTDATAFORMAT structure size %d\n", df->dwObjSize);
     }
-    TRACE("  - dwFlags: 0x%08lx (", df->dwFlags);
+    TRACE("  - dwFlags: 0x%08x (", df->dwFlags);
     switch (df->dwFlags) {
         case DIDF_ABSAXIS: TRACE("DIDF_ABSAXIS"); break;
        case DIDF_RELAXIS: TRACE("DIDF_RELAXIS"); break;
        default: TRACE("unknown"); break;
     }
     TRACE(")\n");
-    TRACE("  - dwDataSize: %ld\n", df->dwDataSize);
-    TRACE("  - dwNumObjs: %ld\n", df->dwNumObjs);
-
+    TRACE("  - dwDataSize: %d\n", df->dwDataSize);
+    TRACE("  - dwNumObjs: %d\n", df->dwNumObjs);
+    
     for (i = 0; i < df->dwNumObjs; i++) {
        TRACE("  - Object %d:\n", i);
        TRACE("      * GUID: %s ('%s')\n", debugstr_guid(df->rgodf[i].pguid), _dump_dinput_GUID(df->rgodf[i].pguid));
-       TRACE("      * dwOfs: %ld\n", df->rgodf[i].dwOfs);
-       TRACE("      * dwType: 0x%08lx\n", df->rgodf[i].dwType);
+        TRACE("      * dwOfs: %d\n", df->rgodf[i].dwOfs);
+        TRACE("      * dwType: 0x%08x\n", df->rgodf[i].dwType);
        TRACE("        "); _dump_EnumObjects_flags(df->rgodf[i].dwType); TRACE("\n");
-       TRACE("      * dwFlags: 0x%08lx\n", df->rgodf[i].dwFlags);
+        TRACE("      * dwFlags: 0x%08x\n", df->rgodf[i].dwFlags);
     }
 }
 
+/******************************************************************************
+ * Get the default and the app-specific config keys.
+ */
+BOOL get_app_key(HKEY *defkey, HKEY *appkey)
+{
+    char buffer[MAX_PATH+16];
+    DWORD len;
+
+    *appkey = 0;
+
+    /* @@ Wine registry key: HKCU\Software\Wine\DirectInput */
+    if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\DirectInput", defkey))
+        *defkey = 0;
+
+    len = GetModuleFileNameA(0, buffer, MAX_PATH);
+    if (len && len < MAX_PATH)
+    {
+        HKEY tmpkey;
+
+        /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DirectInput */
+        if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey))
+        {
+            char *p, *appname = buffer;
+            if ((p = strrchr(appname, '/'))) appname = p + 1;
+            if ((p = strrchr(appname, '\\'))) appname = p + 1;
+            strcat(appname, "\\DirectInput");
+
+            if (RegOpenKeyA(tmpkey, appname, appkey)) *appkey = 0;
+            RegCloseKey(tmpkey);
+        }
+    }
+
+    return *defkey || *appkey;
+}
+
+/******************************************************************************
+ * Get a config key from either the app-specific or the default config
+ */
+DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
+                             char *buffer, DWORD size )
+{
+    if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE)buffer, &size ))
+        return 0;
+
+    if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE)buffer, &size ))
+        return 0;
+
+    return ERROR_FILE_NOT_FOUND;
+}
+
 /* Conversion between internal data buffer and external data buffer */
-void fill_DataFormat(void *out, const void *in, DataFormat *df) {
+void fill_DataFormat(void *out, DWORD size, const void *in, const DataFormat *df)
+{
     int i;
-    char *in_c = (char *) in;
-    char *out_c = (char *) out;
+    const char *in_c = in;
+    char *out_c = out;
 
+    memset(out, 0, size);
     if (df->dt == NULL) {
        /* This means that the app uses Wine's internal data format */
-       memcpy(out, in, df->internal_format_size);
+        memcpy(out, in, min(size, df->internal_format_size));
     } else {
        for (i = 0; i < df->size; i++) {
            if (df->dt[i].offset_in >= 0) {
                switch (df->dt[i].size) {
                    case 1:
                        TRACE("Copying (c) to %d from %d (value %d)\n",
-                             df->dt[i].offset_out, df->dt[i].offset_in, *((char *) (in_c + df->dt[i].offset_in)));
-                       *((char *) (out_c + df->dt[i].offset_out)) = *((char *) (in_c + df->dt[i].offset_in));
+                              df->dt[i].offset_out, df->dt[i].offset_in, *(in_c + df->dt[i].offset_in));
+                       *(out_c + df->dt[i].offset_out) = *(in_c + df->dt[i].offset_in);
                        break;
 
                    case 2:
                        TRACE("Copying (s) to %d from %d (value %d)\n",
-                             df->dt[i].offset_out, df->dt[i].offset_in, *((short *) (in_c + df->dt[i].offset_in)));
-                       *((short *) (out_c + df->dt[i].offset_out)) = *((short *) (in_c + df->dt[i].offset_in));
+                             df->dt[i].offset_out, df->dt[i].offset_in, *((const short *)(in_c + df->dt[i].offset_in)));
+                       *((short *)(out_c + df->dt[i].offset_out)) = *((const short *)(in_c + df->dt[i].offset_in));
                        break;
 
                    case 4:
                        TRACE("Copying (i) to %d from %d (value %d)\n",
-                             df->dt[i].offset_out, df->dt[i].offset_in, *((int *) (in_c + df->dt[i].offset_in)));
-                       *((int *) (out_c + df->dt[i].offset_out)) = *((int *) (in_c + df->dt[i].offset_in));
+                              df->dt[i].offset_out, df->dt[i].offset_in, *((const int *)(in_c + df->dt[i].offset_in)));
+                        *((int *)(out_c + df->dt[i].offset_out)) = *((const int *)(in_c + df->dt[i].offset_in));
                        break;
 
                    default:
@@ -253,23 +306,23 @@ void fill_DataFormat(void *out, const void *in, DataFormat *df) {
                    case 1:
                        TRACE("Copying (c) to %d default value %d\n",
                              df->dt[i].offset_out, df->dt[i].value);
-                       *((char *) (out_c + df->dt[i].offset_out)) = (char) df->dt[i].value;
+                       *(out_c + df->dt[i].offset_out) = (char) df->dt[i].value;
                        break;
-
+                       
                    case 2:
                        TRACE("Copying (s) to %d default value %d\n",
                              df->dt[i].offset_out, df->dt[i].value);
                        *((short *) (out_c + df->dt[i].offset_out)) = (short) df->dt[i].value;
                        break;
-
+                       
                    case 4:
                        TRACE("Copying (i) to %d default value %d\n",
                              df->dt[i].offset_out, df->dt[i].value);
-                       *((int *) (out_c + df->dt[i].offset_out)) = (int) df->dt[i].value;
+                       *((int *) (out_c + df->dt[i].offset_out)) = df->dt[i].value;
                        break;
-
+                       
                    default:
-                       memset((out_c + df->dt[i].offset_out), df->dt[i].size, 0);
+                       memset((out_c + df->dt[i].offset_out), 0, df->dt[i].size);
                        break;
                }
            }
@@ -279,13 +332,24 @@ void fill_DataFormat(void *out, const void *in, DataFormat *df) {
 
 void release_DataFormat(DataFormat * format)
 {
-    TRACE("Deleting DataTransform :\n");
+    TRACE("Deleting DataFormat: %p\n", format);
 
     HeapFree(GetProcessHeap(), 0, format->dt);
+    format->dt = NULL;
+    HeapFree(GetProcessHeap(), 0, format->offsets);
+    format->offsets = NULL;
+    HeapFree(GetProcessHeap(), 0, format->user_df);
+    format->user_df = NULL;
+}
+
+static inline LPDIOBJECTDATAFORMAT dataformat_to_odf(LPCDIDATAFORMAT df, int idx)
+{
+    if (idx < 0 || idx >= df->dwNumObjs) return NULL;
+    return (LPDIOBJECTDATAFORMAT)((LPBYTE)df->rgodf + idx * df->dwObjSize);
 }
 
-DataFormat *create_DataFormat(const DIDATAFORMAT *wine_format, LPCDIDATAFORMAT asked_format, int *offset) {
-    DataFormat *ret;
+static HRESULT create_DataFormat(LPCDIDATAFORMAT asked_format, DataFormat *format)
+{
     DataTransform *dt;
     unsigned int i, j;
     int same = 1;
@@ -293,84 +357,81 @@ DataFormat *create_DataFormat(const DIDATAFORMAT *wine_format, LPCDIDATAFORMAT a
     int index = 0;
     DWORD next = 0;
 
-    ret = HeapAlloc(GetProcessHeap(), 0, sizeof(DataFormat));
+    if (!format->wine_df) return DIERR_INVALIDPARAM;
+    done = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asked_format->dwNumObjs * sizeof(int));
+    dt = HeapAlloc(GetProcessHeap(), 0, asked_format->dwNumObjs * sizeof(DataTransform));
+    if (!dt || !done) goto failed;
 
-    done = HeapAlloc(GetProcessHeap(), 0, sizeof(int) * asked_format->dwNumObjs);
-    memset(done, 0, sizeof(int) * asked_format->dwNumObjs);
+    if (!(format->offsets = HeapAlloc(GetProcessHeap(), 0, format->wine_df->dwNumObjs * sizeof(int))))
+        goto failed;
 
-    dt = HeapAlloc(GetProcessHeap(), 0, asked_format->dwNumObjs * sizeof(DataTransform));
+    if (!(format->user_df = HeapAlloc(GetProcessHeap(), 0, asked_format->dwSize)))
+        goto failed;
+    memcpy(format->user_df, asked_format, asked_format->dwSize);
 
     TRACE("Creating DataTransform :\n");
-
-    for (i = 0; i < wine_format->dwNumObjs; i++) {
-       offset[i] = -1;
+    
+    for (i = 0; i < format->wine_df->dwNumObjs; i++)
+    {
+        format->offsets[i] = -1;
 
        for (j = 0; j < asked_format->dwNumObjs; j++) {
            if (done[j] == 1)
                continue;
-
+           
            if (/* Check if the application either requests any GUID and if not, it if matches
                 * the GUID of the Wine object.
                 */
                ((asked_format->rgodf[j].pguid == NULL) ||
-                (wine_format->rgodf[i].pguid == NULL) ||
-                (IsEqualGUID(wine_format->rgodf[i].pguid, asked_format->rgodf[j].pguid)))
+                (format->wine_df->rgodf[i].pguid == NULL) ||
+                (IsEqualGUID(format->wine_df->rgodf[i].pguid, asked_format->rgodf[j].pguid)))
                &&
                (/* Then check if it accepts any instance id, and if not, if it matches Wine's
                  * instance id.
                  */
-                (DIDFT_GETINSTANCE(asked_format->rgodf[j].dwType) == 0xFFFF) ||
+                ((asked_format->rgodf[j].dwType & DIDFT_INSTANCEMASK) == DIDFT_ANYINSTANCE) ||
                 (DIDFT_GETINSTANCE(asked_format->rgodf[j].dwType) == 0x00FF) || /* This is mentionned in no DX docs, but it works fine - tested on WinXP */
-                (DIDFT_GETINSTANCE(asked_format->rgodf[j].dwType) == DIDFT_GETINSTANCE(wine_format->rgodf[i].dwType)))
+                (DIDFT_GETINSTANCE(asked_format->rgodf[j].dwType) == DIDFT_GETINSTANCE(format->wine_df->rgodf[i].dwType)))
                &&
                ( /* Then if the asked type matches the one Wine provides */
-                wine_format->rgodf[i].dwType & asked_format->rgodf[j].dwType)) {
-
+                 DIDFT_GETTYPE(asked_format->rgodf[j].dwType) & format->wine_df->rgodf[i].dwType))
+            {
                done[j] = 1;
-
+               
                TRACE("Matching :\n");
                TRACE("   - Asked (%d) :\n", j);
                TRACE("       * GUID: %s ('%s')\n",
                      debugstr_guid(asked_format->rgodf[j].pguid),
                      _dump_dinput_GUID(asked_format->rgodf[j].pguid));
-               TRACE("       * Offset: %3ld\n", asked_format->rgodf[j].dwOfs);
-               TRACE("       * dwType: %08lx\n", asked_format->rgodf[j].dwType);
+                TRACE("       * Offset: %3d\n", asked_format->rgodf[j].dwOfs);
+                TRACE("       * dwType: %08x\n", asked_format->rgodf[j].dwType);
                TRACE("         "); _dump_EnumObjects_flags(asked_format->rgodf[j].dwType); TRACE("\n");
-
+               
                TRACE("   - Wine  (%d) :\n", i);
                TRACE("       * GUID: %s ('%s')\n",
-                     debugstr_guid(wine_format->rgodf[i].pguid),
-                     _dump_dinput_GUID(wine_format->rgodf[i].pguid));
-               TRACE("       * Offset: %3ld\n", wine_format->rgodf[i].dwOfs);
-               TRACE("       * dwType: %08lx\n", wine_format->rgodf[i].dwType);
-               TRACE("         "); _dump_EnumObjects_flags(wine_format->rgodf[i].dwType); TRACE("\n");
-
-               if (wine_format->rgodf[i].dwType & DIDFT_BUTTON)
+                      debugstr_guid(format->wine_df->rgodf[i].pguid),
+                      _dump_dinput_GUID(format->wine_df->rgodf[i].pguid));
+                TRACE("       * Offset: %3d\n", format->wine_df->rgodf[i].dwOfs);
+                TRACE("       * dwType: %08x\n", format->wine_df->rgodf[i].dwType);
+                TRACE("         "); _dump_EnumObjects_flags(format->wine_df->rgodf[i].dwType); TRACE("\n");
+               
+                if (format->wine_df->rgodf[i].dwType & DIDFT_BUTTON)
                    dt[index].size = sizeof(BYTE);
                else
                    dt[index].size = sizeof(DWORD);
-               dt[index].offset_in = wine_format->rgodf[i].dwOfs;
-                if (asked_format->rgodf[j].dwOfs < next) {
-                    WARN("bad format: dwOfs=%ld, changing to %ld\n", asked_format->rgodf[j].dwOfs, next);
-                   dt[index].offset_out = next;
-                   offset[i] = next;
-                } else {
-                   dt[index].offset_out = asked_format->rgodf[j].dwOfs;
-                    offset[i] = asked_format->rgodf[j].dwOfs;
-                }
+                dt[index].offset_in = format->wine_df->rgodf[i].dwOfs;
+                dt[index].offset_out = asked_format->rgodf[j].dwOfs;
+                format->offsets[i]   = asked_format->rgodf[j].dwOfs;
                dt[index].value = 0;
                 next = next + dt[index].size;
-
-               if (wine_format->rgodf[i].dwOfs != dt[index].offset_out)
+               
+                if (format->wine_df->rgodf[i].dwOfs != dt[index].offset_out)
                    same = 0;
-
+               
                index++;
                break;
            }
        }
-
-       if (j == asked_format->dwNumObjs)
-           same = 0;
     }
 
     TRACE("Setting to default value :\n");
@@ -380,68 +441,170 @@ DataFormat *create_DataFormat(const DIDATAFORMAT *wine_format, LPCDIDATAFORMAT a
            TRACE("       * GUID: %s ('%s')\n",
                  debugstr_guid(asked_format->rgodf[j].pguid),
                  _dump_dinput_GUID(asked_format->rgodf[j].pguid));
-           TRACE("       * Offset: %3ld\n", asked_format->rgodf[j].dwOfs);
-           TRACE("       * dwType: %08lx\n", asked_format->rgodf[j].dwType);
+            TRACE("       * Offset: %3d\n", asked_format->rgodf[j].dwOfs);
+            TRACE("       * dwType: %08x\n", asked_format->rgodf[j].dwType);
            TRACE("         "); _dump_EnumObjects_flags(asked_format->rgodf[j].dwType); TRACE("\n");
-
+           
            if (asked_format->rgodf[j].dwType & DIDFT_BUTTON)
                dt[index].size = sizeof(BYTE);
            else
                dt[index].size = sizeof(DWORD);
            dt[index].offset_in  = -1;
            dt[index].offset_out = asked_format->rgodf[j].dwOfs;
-           dt[index].value = 0;
+            if (asked_format->rgodf[j].dwType & DIDFT_POV)
+                dt[index].value = -1;
+            else
+                dt[index].value = 0;
            index++;
 
            same = 0;
        }
     }
-
-    ret->internal_format_size = wine_format->dwDataSize;
-    ret->size = index;
+    
+    format->internal_format_size = format->wine_df->dwDataSize;
+    format->size = index;
     if (same) {
-       ret->dt = NULL;
        HeapFree(GetProcessHeap(), 0, dt);
-    } else {
-       ret->dt = dt;
+        dt = NULL;
     }
+    format->dt = dt;
 
     HeapFree(GetProcessHeap(), 0, done);
 
-    return ret;
+    return DI_OK;
+
+failed:
+    HeapFree(GetProcessHeap(), 0, done);
+    HeapFree(GetProcessHeap(), 0, dt);
+    format->dt = NULL;
+    HeapFree(GetProcessHeap(), 0, format->offsets);
+    format->offsets = NULL;
+    HeapFree(GetProcessHeap(), 0, format->user_df);
+    format->user_df = NULL;
+
+    return DIERR_OUTOFMEMORY;
 }
 
-BOOL DIEnumDevicesCallbackAtoW(LPCDIDEVICEOBJECTINSTANCEA lpddi, LPVOID lpvRef) {
-    DIDEVICEOBJECTINSTANCEW ddtmp;
-    device_enumobjects_AtoWcb_data* data;
+/* find an object by it's offset in a data format */
+static int offset_to_object(const DataFormat *df, int offset)
+{
+    int i;
 
-    data = (device_enumobjects_AtoWcb_data*) lpvRef;
+    if (!df->offsets) return -1;
 
-    memset(&ddtmp, 0, sizeof(ddtmp));
+    for (i = 0; i < df->wine_df->dwNumObjs; i++)
+        if (df->offsets[i] == offset) return i;
 
-    ddtmp.dwSize = sizeof(DIDEVICEINSTANCEW);
-    ddtmp.guidType     = lpddi->guidType;
-    ddtmp.dwOfs        = lpddi->dwOfs;
-    ddtmp.dwType       = lpddi->dwType;
-    ddtmp.dwFlags      = lpddi->dwFlags;
-    MultiByteToWideChar(CP_ACP, 0, lpddi->tszName, -1, ddtmp.tszName, MAX_PATH);
+    return -1;
+}
 
-    if (lpddi->dwSize == sizeof(DIDEVICEINSTANCEA)) {
-       /**
-        * if dwSize < sizeof(DIDEVICEINSTANCEA of DInput version >= 5)
-        *  force feedback and other newer datas aren't available
-        */
-       ddtmp.dwFFMaxForce        = lpddi->dwFFMaxForce;
-       ddtmp.dwFFForceResolution = lpddi->dwFFForceResolution;
-       ddtmp.wCollectionNumber   = lpddi->wCollectionNumber;
-       ddtmp.wDesignatorIndex    = lpddi->wDesignatorIndex;
-       ddtmp.wUsagePage          = lpddi->wUsagePage;
-       ddtmp.wUsage              = lpddi->wUsage;
-       ddtmp.dwDimension         = lpddi->dwDimension;
-       ddtmp.wExponent           = lpddi->wExponent;
-       ddtmp.wReserved           = lpddi->wReserved;
+int id_to_object(LPCDIDATAFORMAT df, int id)
+{
+    int i;
+
+    id &= 0x00ffffff;
+    for (i = 0; i < df->dwNumObjs; i++)
+        if ((dataformat_to_odf(df, i)->dwType & 0x00ffffff) == id)
+            return i;
+
+    return -1;
+}
+
+int id_to_offset(const DataFormat *df, int id)
+{
+    int obj = id_to_object(df->wine_df, id);
+
+    return obj >= 0 && df->offsets ? df->offsets[obj] : -1;
+}
+
+int find_property(const DataFormat *df, LPCDIPROPHEADER ph)
+{
+    switch (ph->dwHow)
+    {
+        case DIPH_BYID:     return id_to_object(df->wine_df, ph->dwObj);
+        case DIPH_BYOFFSET: return offset_to_object(df, ph->dwObj);
+    }
+    FIXME("Unhandled ph->dwHow=='%04X'\n", (unsigned int)ph->dwHow);
+
+    return -1;
+}
+
+/******************************************************************************
+ *     queue_event - add new event to the ring queue
+ */
+
+void queue_event(LPDIRECTINPUTDEVICE8A iface, int ofs, DWORD data, DWORD time, DWORD seq)
+{
+    IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
+    int next_pos;
+
+    /* Event is being set regardless of the queue state */
+    if (This->hEvent) SetEvent(This->hEvent);
+
+    if (!This->queue_len || This->overflow || ofs < 0) return;
+
+    next_pos = (This->queue_head + 1) % This->queue_len;
+    if (next_pos == This->queue_tail)
+    {
+        TRACE(" queue overflowed\n");
+        This->overflow = TRUE;
+        return;
     }
-    return data->lpCallBack(&ddtmp, data->lpvRef);
+
+    TRACE(" queueing %d at offset %d (queue head %d / size %d)\n",
+          data, ofs, This->queue_head, This->queue_len);
+
+    This->data_queue[This->queue_head].dwOfs       = ofs;
+    This->data_queue[This->queue_head].dwData      = data;
+    This->data_queue[This->queue_head].dwTimeStamp = time;
+    This->data_queue[This->queue_head].dwSequence  = seq;
+    This->queue_head = next_pos;
+    /* Send event if asked */
+}
+
+/******************************************************************************
+ *     Acquire
+ */
+
+HRESULT WINAPI IDirectInputDevice2AImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
+{
+    IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
+    HRESULT res;
+
+    if (!This->data_format.user_df) return DIERR_INVALIDPARAM;
+    if (This->dwCoopLevel & DISCL_FOREGROUND && This->win != GetForegroundWindow())
+        return DIERR_OTHERAPPHASPRIO;
+
+    EnterCriticalSection(&This->crit);
+    res = This->acquired ? S_FALSE : DI_OK;
+    This->acquired = 1;
+    if (res == DI_OK)
+    {
+        This->queue_head = This->queue_tail = This->overflow = 0;
+        check_dinput_hooks(iface);
+    }
+    LeaveCriticalSection(&This->crit);
+
+    return res;
+}
+
+/******************************************************************************
+ *     Unacquire
+ */
+
+HRESULT WINAPI IDirectInputDevice2AImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
+{
+    IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
+    HRESULT res;
+
+    EnterCriticalSection(&This->crit);
+    res = !This->acquired ? DI_NOEFFECT : DI_OK;
+    This->acquired = 0;
+    if (res == DI_OK)
+        check_dinput_hooks(iface);
+    LeaveCriticalSection(&This->crit);
+
+    return res;
 }
 
 /******************************************************************************
@@ -449,34 +612,80 @@ BOOL DIEnumDevicesCallbackAtoW(LPCDIDEVICEOBJECTINSTANCEA lpddi, LPVOID lpvRef)
  */
 
 HRESULT WINAPI IDirectInputDevice2AImpl_SetDataFormat(
-       LPDIRECTINPUTDEVICE8A iface,LPCDIDATAFORMAT df
-{
+        LPDIRECTINPUTDEVICE8A iface, LPCDIDATAFORMAT df)
+{
     IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
+    HRESULT res = DI_OK;
 
-    TRACE("(this=%p,%p)\n",This,df);
-
+    if (!df) return E_POINTER;
+    TRACE("(%p) %p\n", This, df);
     _dump_DIDATAFORMAT(df);
 
-    return DI_OK;
+    if (df->dwSize != sizeof(DIDATAFORMAT)) return DIERR_INVALIDPARAM;
+    if (This->acquired) return DIERR_ACQUIRED;
+
+    EnterCriticalSection(&This->crit);
+
+    release_DataFormat(&This->data_format);
+    res = create_DataFormat(df, &This->data_format);
+
+    LeaveCriticalSection(&This->crit);
+    return res;
 }
 
+/******************************************************************************
+  *     SetCooperativeLevel
+  *
+  *  Set cooperative level and the source window for the events.
+  */
 HRESULT WINAPI IDirectInputDevice2AImpl_SetCooperativeLevel(
-       LPDIRECTINPUTDEVICE8A iface,HWND hwnd,DWORD dwflags
-{
+        LPDIRECTINPUTDEVICE8A iface, HWND hwnd, DWORD dwflags)
+{
     IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
-    TRACE("(this=%p,%p,0x%08lx)\n",This,hwnd,dwflags);
-    if (TRACE_ON(dinput)) {
-       TRACE(" cooperative level : ");
-       _dump_cooperativelevel_DI(dwflags);
-    }
+
+    TRACE("(%p) %p,0x%08x\n", This, hwnd, dwflags);
+    _dump_cooperativelevel_DI(dwflags);
+
+    if ((dwflags & (DISCL_EXCLUSIVE | DISCL_NONEXCLUSIVE)) == 0 ||
+        (dwflags & (DISCL_EXCLUSIVE | DISCL_NONEXCLUSIVE)) == (DISCL_EXCLUSIVE | DISCL_NONEXCLUSIVE) ||
+        (dwflags & (DISCL_FOREGROUND | DISCL_BACKGROUND)) == 0 ||
+        (dwflags & (DISCL_FOREGROUND | DISCL_BACKGROUND)) == (DISCL_FOREGROUND | DISCL_BACKGROUND))
+        return DIERR_INVALIDPARAM;
+
+    if (dwflags == (DISCL_NONEXCLUSIVE | DISCL_BACKGROUND))
+        hwnd = GetDesktopWindow();
+
+    if (!hwnd) return E_HANDLE;
+
+    /* For security reasons native does not allow exclusive background level
+       for mouse and keyboard only */
+    if (dwflags & DISCL_EXCLUSIVE && dwflags & DISCL_BACKGROUND &&
+        (IsEqualGUID(&This->guid, &GUID_SysMouse) ||
+         IsEqualGUID(&This->guid, &GUID_SysKeyboard)))
+        return DIERR_UNSUPPORTED;
+
+    /* Store the window which asks for the mouse */
+    EnterCriticalSection(&This->crit);
+    This->win = hwnd;
+    This->dwCoopLevel = dwflags;
+    LeaveCriticalSection(&This->crit);
+
     return DI_OK;
 }
 
+/******************************************************************************
+  *     SetEventNotification : specifies event to be sent on state change
+  */
 HRESULT WINAPI IDirectInputDevice2AImpl_SetEventNotification(
-       LPDIRECTINPUTDEVICE8A iface,HANDLE hnd
-{
+       LPDIRECTINPUTDEVICE8A iface, HANDLE event)
+{
     IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
-    FIXME("(this=%p,%p): stub\n",This,hnd);
+
+    TRACE("(%p) %p\n", This, event);
+
+    EnterCriticalSection(&This->crit);
+    This->hEvent = event;
+    LeaveCriticalSection(&This->crit);
     return DI_OK;
 }
 
@@ -484,10 +693,32 @@ ULONG WINAPI IDirectInputDevice2AImpl_Release(LPDIRECTINPUTDEVICE8A iface)
 {
     IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
     ULONG ref;
+
     ref = InterlockedDecrement(&(This->ref));
-    if (ref == 0)
-        HeapFree(GetProcessHeap(),0,This);
-    return ref;
+    if (ref) return ref;
+
+    IDirectInputDevice_Unacquire(iface);
+    /* Reset the FF state, free all effects, etc */
+    IDirectInputDevice8_SendForceFeedbackCommand(iface, DISFFC_RESET);
+
+    HeapFree(GetProcessHeap(), 0, This->data_queue);
+
+    /* Free data format */
+    HeapFree(GetProcessHeap(), 0, This->data_format.wine_df->rgodf);
+    HeapFree(GetProcessHeap(), 0, This->data_format.wine_df);
+    release_DataFormat(&This->data_format);
+
+    EnterCriticalSection( &This->dinput->crit );
+    list_remove( &This->entry );
+    LeaveCriticalSection( &This->dinput->crit );
+
+    IDirectInput_Release((LPDIRECTINPUTDEVICE8A)This->dinput);
+    This->crit.DebugInfo->Spare[0] = 0;
+    DeleteCriticalSection(&This->crit);
+
+    HeapFree(GetProcessHeap(), 0, This);
+
+    return DI_OK;
 }
 
 HRESULT WINAPI IDirectInputDevice2AImpl_QueryInterface(
@@ -495,7 +726,7 @@ HRESULT WINAPI IDirectInputDevice2AImpl_QueryInterface(
 )
 {
     IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
-
+    
     TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
     if (IsEqualGUID(&IID_IUnknown,riid)) {
        IDirectInputDevice2_AddRef(iface);
@@ -531,7 +762,7 @@ HRESULT WINAPI IDirectInputDevice2WImpl_QueryInterface(
 )
 {
     IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
-
+    
     TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
     if (IsEqualGUID(&IID_IUnknown,riid)) {
        IDirectInputDevice2_AddRef(iface);
@@ -569,48 +800,160 @@ ULONG WINAPI IDirectInputDevice2AImpl_AddRef(
     return InterlockedIncrement(&(This->ref));
 }
 
-HRESULT WINAPI IDirectInputDevice2AImpl_EnumObjects(
-       LPDIRECTINPUTDEVICE8A iface,
-       LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback,
-       LPVOID lpvRef,
-       DWORD dwFlags)
+HRESULT WINAPI IDirectInputDevice2AImpl_EnumObjects(LPDIRECTINPUTDEVICE8A iface,
+        LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback, LPVOID lpvRef, DWORD dwFlags)
 {
-    FIXME("(this=%p,%p,%p,%08lx): stub!\n", iface, lpCallback, lpvRef, dwFlags);
-    if (TRACE_ON(dinput)) {
-       DPRINTF("  - flags = ");
-       _dump_EnumObjects_flags(dwFlags);
-       DPRINTF("\n");
+    IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
+    DIDEVICEOBJECTINSTANCEA ddoi;
+    int i;
+
+    TRACE("(%p) %p,%p flags:%08x)\n", iface, lpCallback, lpvRef, dwFlags);
+    TRACE("  - flags = ");
+    _dump_EnumObjects_flags(dwFlags);
+    TRACE("\n");
+
+    /* Only the fields till dwFFMaxForce are relevant */
+    memset(&ddoi, 0, sizeof(ddoi));
+    ddoi.dwSize = FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA, dwFFMaxForce);
+
+    for (i = 0; i < This->data_format.wine_df->dwNumObjs; i++)
+    {
+        LPDIOBJECTDATAFORMAT odf = dataformat_to_odf(This->data_format.wine_df, i);
+
+        if (dwFlags != DIDFT_ALL && !(dwFlags & DIEFT_GETTYPE(odf->dwType))) continue;
+        if (IDirectInputDevice_GetObjectInfo(iface, &ddoi, odf->dwType, DIPH_BYID) != DI_OK)
+            continue;
+
+       if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) break;
     }
 
     return DI_OK;
 }
 
-HRESULT WINAPI IDirectInputDevice2WImpl_EnumObjects(
-       LPDIRECTINPUTDEVICE8W iface,
-       LPDIENUMDEVICEOBJECTSCALLBACKW lpCallback,
-       LPVOID lpvRef,
-       DWORD dwFlags)
+HRESULT WINAPI IDirectInputDevice2WImpl_EnumObjects(LPDIRECTINPUTDEVICE8W iface,
+        LPDIENUMDEVICEOBJECTSCALLBACKW lpCallback, LPVOID lpvRef, DWORD dwFlags)
 {
-    FIXME("(this=%p,%p,%p,%08lx): stub!\n", iface, lpCallback, lpvRef, dwFlags);
-    if (TRACE_ON(dinput)) {
-       DPRINTF("  - flags = ");
-       _dump_EnumObjects_flags(dwFlags);
-       DPRINTF("\n");
+    IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
+    DIDEVICEOBJECTINSTANCEW ddoi;
+    int i;
+
+    TRACE("(%p) %p,%p flags:%08x)\n", iface, lpCallback, lpvRef, dwFlags);
+    TRACE("  - flags = ");
+    _dump_EnumObjects_flags(dwFlags);
+    TRACE("\n");
+
+    /* Only the fields till dwFFMaxForce are relevant */
+    memset(&ddoi, 0, sizeof(ddoi));
+    ddoi.dwSize = FIELD_OFFSET(DIDEVICEOBJECTINSTANCEW, dwFFMaxForce);
+
+    for (i = 0; i < This->data_format.wine_df->dwNumObjs; i++)
+    {
+        LPDIOBJECTDATAFORMAT odf = dataformat_to_odf(This->data_format.wine_df, i);
+
+        if (dwFlags != DIDFT_ALL && !(dwFlags & DIEFT_GETTYPE(odf->dwType))) continue;
+        if (IDirectInputDevice_GetObjectInfo(iface, &ddoi, odf->dwType, DIPH_BYID) != DI_OK)
+            continue;
+
+       if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) break;
     }
 
     return DI_OK;
 }
 
+/******************************************************************************
+ *     GetProperty
+ */
+
 HRESULT WINAPI IDirectInputDevice2AImpl_GetProperty(
-       LPDIRECTINPUTDEVICE8A iface,
-       REFGUID rguid,
-       LPDIPROPHEADER pdiph)
+       LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPDIPROPHEADER pdiph)
+{
+    IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
+
+    TRACE("(%p) %s,%p\n", iface, debugstr_guid(rguid), pdiph);
+    _dump_DIPROPHEADER(pdiph);
+
+    if (HIWORD(rguid)) return DI_OK;
+
+    switch (LOWORD(rguid))
+    {
+        case (DWORD_PTR) DIPROP_BUFFERSIZE:
+        {
+            LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
+
+            if (pdiph->dwSize != sizeof(DIPROPDWORD)) return DIERR_INVALIDPARAM;
+
+            pd->dwData = This->queue_len;
+            TRACE("buffersize = %d\n", pd->dwData);
+            break;
+        }
+        default:
+            WARN("Unknown property %s\n", debugstr_guid(rguid));
+            break;
+    }
+
+    return DI_OK;
+}
+
+/******************************************************************************
+ *     SetProperty
+ */
+
+HRESULT WINAPI IDirectInputDevice2AImpl_SetProperty(
+        LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPCDIPROPHEADER pdiph)
 {
-    FIXME("(this=%p,%s,%p): stub!\n",
-         iface, debugstr_guid(rguid), pdiph);
+    IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
 
-    if (TRACE_ON(dinput))
-       _dump_DIPROPHEADER(pdiph);
+    TRACE("(%p) %s,%p\n", iface, debugstr_guid(rguid), pdiph);
+    _dump_DIPROPHEADER(pdiph);
+
+    if (HIWORD(rguid)) return DI_OK;
+
+    switch (LOWORD(rguid))
+    {
+        case (DWORD_PTR) DIPROP_AXISMODE:
+        {
+            LPCDIPROPDWORD pd = (LPCDIPROPDWORD)pdiph;
+
+            if (pdiph->dwSize != sizeof(DIPROPDWORD)) return DIERR_INVALIDPARAM;
+            if (pdiph->dwHow == DIPH_DEVICE && pdiph->dwObj) return DIERR_INVALIDPARAM;
+            if (This->acquired) return DIERR_ACQUIRED;
+            if (pdiph->dwHow != DIPH_DEVICE) return DIERR_UNSUPPORTED;
+            if (!This->data_format.user_df) return DI_OK;
+
+            TRACE("Axis mode: %s\n", pd->dwData == DIPROPAXISMODE_ABS ? "absolute" :
+                                                                        "relative");
+
+            EnterCriticalSection(&This->crit);
+            This->data_format.user_df->dwFlags &= ~DIDFT_AXIS;
+            This->data_format.user_df->dwFlags |= pd->dwData == DIPROPAXISMODE_ABS ?
+                                                  DIDF_ABSAXIS : DIDF_RELAXIS;
+            LeaveCriticalSection(&This->crit);
+            break;
+        }
+        case (DWORD_PTR) DIPROP_BUFFERSIZE:
+        {
+            LPCDIPROPDWORD pd = (LPCDIPROPDWORD)pdiph;
+
+            if (pdiph->dwSize != sizeof(DIPROPDWORD)) return DIERR_INVALIDPARAM;
+            if (This->acquired) return DIERR_ACQUIRED;
+
+            TRACE("buffersize = %d\n", pd->dwData);
+
+            EnterCriticalSection(&This->crit);
+            HeapFree(GetProcessHeap(), 0, This->data_queue);
+
+            This->data_queue = !pd->dwData ? NULL : HeapAlloc(GetProcessHeap(), 0,
+                                pd->dwData * sizeof(DIDEVICEOBJECTDATA));
+            This->queue_head = This->queue_tail = This->overflow = 0;
+            This->queue_len  = pd->dwData;
+
+            LeaveCriticalSection(&This->crit);
+            break;
+        }
+        default:
+            WARN("Unknown property %s\n", debugstr_guid(rguid));
+            return DIERR_UNSUPPORTED;
+    }
 
     return DI_OK;
 }
@@ -621,10 +964,29 @@ HRESULT WINAPI IDirectInputDevice2AImpl_GetObjectInfo(
        DWORD dwObj,
        DWORD dwHow)
 {
-    FIXME("(this=%p,%p,%ld,0x%08lx): stub!\n",
-         iface, pdidoi, dwObj, dwHow);
+    DIDEVICEOBJECTINSTANCEW didoiW;
+    HRESULT res;
+
+    if (!pdidoi ||
+        (pdidoi->dwSize != sizeof(DIDEVICEOBJECTINSTANCEA) &&
+         pdidoi->dwSize != sizeof(DIDEVICEOBJECTINSTANCE_DX3A)))
+        return DIERR_INVALIDPARAM;
+
+    didoiW.dwSize = sizeof(didoiW);
+    res = IDirectInputDevice2WImpl_GetObjectInfo((LPDIRECTINPUTDEVICE8W)iface, &didoiW, dwObj, dwHow);
+    if (res == DI_OK)
+    {
+        DWORD dwSize = pdidoi->dwSize;
+
+        memset(pdidoi, 0, pdidoi->dwSize);
+        pdidoi->dwSize   = dwSize;
+        pdidoi->guidType = didoiW.guidType;
+        pdidoi->dwOfs    = didoiW.dwOfs;
+        pdidoi->dwType   = didoiW.dwType;
+        pdidoi->dwFlags  = didoiW.dwFlags;
+    }
 
-    return DI_OK;
+    return res;
 }
 
 HRESULT WINAPI IDirectInputDevice2WImpl_GetObjectInfo(
@@ -633,30 +995,104 @@ HRESULT WINAPI IDirectInputDevice2WImpl_GetObjectInfo(
        DWORD dwObj,
        DWORD dwHow)
 {
-    FIXME("(this=%p,%p,%ld,0x%08lx): stub!\n",
-         iface, pdidoi, dwObj, dwHow);
+    IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
+    DWORD dwSize;
+    LPDIOBJECTDATAFORMAT odf;
+    int idx = -1;
+
+    TRACE("(%p) %d(0x%08x) -> %p\n", This, dwHow, dwObj, pdidoi);
+
+    if (!pdidoi ||
+        (pdidoi->dwSize != sizeof(DIDEVICEOBJECTINSTANCEW) &&
+         pdidoi->dwSize != sizeof(DIDEVICEOBJECTINSTANCE_DX3W)))
+        return DIERR_INVALIDPARAM;
+
+    switch (dwHow)
+    {
+    case DIPH_BYOFFSET:
+        if (!This->data_format.offsets) break;
+        for (idx = This->data_format.wine_df->dwNumObjs - 1; idx >= 0; idx--)
+            if (This->data_format.offsets[idx] == dwObj) break;
+        break;
+    case DIPH_BYID:
+        dwObj &= 0x00ffffff;
+        for (idx = This->data_format.wine_df->dwNumObjs - 1; idx >= 0; idx--)
+            if ((dataformat_to_odf(This->data_format.wine_df, idx)->dwType & 0x00ffffff) == dwObj)
+                break;
+        break;
+
+    case DIPH_BYUSAGE:
+        FIXME("dwHow = DIPH_BYUSAGE not implemented\n");
+        break;
+    default:
+        WARN("invalid parameter: dwHow = %08x\n", dwHow);
+        return DIERR_INVALIDPARAM;
+    }
+    if (idx < 0) return DIERR_OBJECTNOTFOUND;
+
+    odf = dataformat_to_odf(This->data_format.wine_df, idx);
+    dwSize = pdidoi->dwSize; /* save due to memset below */
+    memset(pdidoi, 0, pdidoi->dwSize);
+    pdidoi->dwSize   = dwSize;
+    if (odf->pguid) pdidoi->guidType = *odf->pguid;
+    pdidoi->dwOfs    = This->data_format.offsets ? This->data_format.offsets[idx] : odf->dwOfs;
+    pdidoi->dwType   = odf->dwType;
+    pdidoi->dwFlags  = odf->dwFlags;
 
     return DI_OK;
 }
 
-HRESULT WINAPI IDirectInputDevice2AImpl_GetDeviceInfo(
-       LPDIRECTINPUTDEVICE8A iface,
-       LPDIDEVICEINSTANCEA pdidi)
+HRESULT WINAPI IDirectInputDevice2AImpl_GetDeviceData(
+        LPDIRECTINPUTDEVICE8A iface, DWORD dodsize, LPDIDEVICEOBJECTDATA dod,
+        LPDWORD entries, DWORD flags)
 {
-    FIXME("(this=%p,%p): stub!\n",
-         iface, pdidi);
+    IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
+    HRESULT ret = DI_OK;
+    int len;
+
+    TRACE("(%p) %p -> %p(%d) x%d, 0x%08x\n",
+          This, dod, entries, entries ? *entries : 0, dodsize, flags);
+
+    if (!This->acquired)
+        return DIERR_NOTACQUIRED;
+    if (!This->queue_len)
+        return DIERR_NOTBUFFERED;
+    if (dodsize < sizeof(DIDEVICEOBJECTDATA_DX3))
+        return DIERR_INVALIDPARAM;
+
+    IDirectInputDevice2_Poll(iface);
+    EnterCriticalSection(&This->crit);
+
+    len = This->queue_head - This->queue_tail;
+    if (len < 0) len += This->queue_len;
+
+    if ((*entries != INFINITE) && (len > *entries)) len = *entries;
+
+    if (dod)
+    {
+        int i;
+        for (i = 0; i < len; i++)
+        {
+            int n = (This->queue_tail + i) % This->queue_len;
+            memcpy((char *)dod + dodsize * i, This->data_queue + n, dodsize);
+        }
+    }
+    *entries = len;
 
-    return DI_OK;
-}
+    if (This->overflow)
+        ret = DI_BUFFEROVERFLOW;
 
-HRESULT WINAPI IDirectInputDevice2WImpl_GetDeviceInfo(
-       LPDIRECTINPUTDEVICE8W iface,
-       LPDIDEVICEINSTANCEW pdidi)
-{
-    FIXME("(this=%p,%p): stub!\n",
-         iface, pdidi);
+    if (!(flags & DIGDD_PEEK))
+    {
+        /* Advance reading position */
+        This->queue_tail = (This->queue_tail + len) % This->queue_len;
+        This->overflow = FALSE;
+    }
 
-    return DI_OK;
+    LeaveCriticalSection(&This->crit);
+
+    TRACE("Returning %d events queued\n", *entries);
+    return ret;
 }
 
 HRESULT WINAPI IDirectInputDevice2AImpl_RunControlPanel(
@@ -664,7 +1100,7 @@ HRESULT WINAPI IDirectInputDevice2AImpl_RunControlPanel(
        HWND hwndOwner,
        DWORD dwFlags)
 {
-    FIXME("(this=%p,%p,0x%08lx): stub!\n",
+    FIXME("(this=%p,%p,0x%08x): stub!\n",
          iface, hwndOwner, dwFlags);
 
     return DI_OK;
@@ -676,7 +1112,7 @@ HRESULT WINAPI IDirectInputDevice2AImpl_Initialize(
        DWORD dwVersion,
        REFGUID rguid)
 {
-    FIXME("(this=%p,%p,%ld,%s): stub!\n",
+    FIXME("(this=%p,%p,%d,%s): stub!\n",
          iface, hinst, dwVersion, debugstr_guid(rguid));
     return DI_OK;
 }
@@ -703,9 +1139,9 @@ HRESULT WINAPI IDirectInputDevice2AImpl_EnumEffects(
        LPVOID lpvRef,
        DWORD dwFlags)
 {
-    FIXME("(this=%p,%p,%p,0x%08lx): stub!\n",
+    FIXME("(this=%p,%p,%p,0x%08x): stub!\n",
          iface, lpCallback, lpvRef, dwFlags);
-
+    
     return DI_OK;
 }
 
@@ -715,9 +1151,9 @@ HRESULT WINAPI IDirectInputDevice2WImpl_EnumEffects(
        LPVOID lpvRef,
        DWORD dwFlags)
 {
-    FIXME("(this=%p,%p,%p,0x%08lx): stub!\n",
+    FIXME("(this=%p,%p,%p,0x%08x): stub!\n",
          iface, lpCallback, lpvRef, dwFlags);
-
+    
     return DI_OK;
 }
 
@@ -754,9 +1190,8 @@ HRESULT WINAPI IDirectInputDevice2AImpl_SendForceFeedbackCommand(
        LPDIRECTINPUTDEVICE8A iface,
        DWORD dwFlags)
 {
-    FIXME("(this=%p,0x%08lx): stub!\n",
-         iface, dwFlags);
-    return DI_OK;
+    TRACE("(%p) 0x%08x:\n", iface, dwFlags);
+    return DI_NOEFFECT;
 }
 
 HRESULT WINAPI IDirectInputDevice2AImpl_EnumCreatedEffectObjects(
@@ -765,7 +1200,7 @@ HRESULT WINAPI IDirectInputDevice2AImpl_EnumCreatedEffectObjects(
        LPVOID lpvRef,
        DWORD dwFlags)
 {
-    FIXME("(this=%p,%p,%p,0x%08lx): stub!\n",
+    FIXME("(this=%p,%p,%p,0x%08x): stub!\n",
          iface, lpCallback, lpvRef, dwFlags);
     return DI_OK;
 }
@@ -782,6 +1217,9 @@ HRESULT WINAPI IDirectInputDevice2AImpl_Escape(
 HRESULT WINAPI IDirectInputDevice2AImpl_Poll(
        LPDIRECTINPUTDEVICE8A iface)
 {
+    IDirectInputDevice2AImpl *This = (IDirectInputDevice2AImpl *)iface;
+
+    if (!This->acquired) return DIERR_NOTACQUIRED;
     /* Because wine devices do not need to be polled, just return DI_NOEFFECT */
     return DI_NOEFFECT;
 }
@@ -793,9 +1231,9 @@ HRESULT WINAPI IDirectInputDevice2AImpl_SendDeviceData(
        LPDWORD pdwInOut,
        DWORD dwFlags)
 {
-    FIXME("(this=%p,0x%08lx,%p,%p,0x%08lx): stub!\n",
+    FIXME("(this=%p,0x%08x,%p,%p,0x%08x): stub!\n",
          iface, cbObjectData, rgdod, pdwInOut, dwFlags);
-
+    
     return DI_OK;
 }
 
@@ -805,8 +1243,8 @@ HRESULT WINAPI IDirectInputDevice7AImpl_EnumEffectsInFile(LPDIRECTINPUTDEVICE8A
                                                          LPVOID pvRef,
                                                          DWORD dwFlags)
 {
-    FIXME("(%p)->(%s,%p,%p,%08lx): stub !\n", iface, lpszFileName, pec, pvRef, dwFlags);
-
+    FIXME("(%p)->(%s,%p,%p,%08x): stub !\n", iface, lpszFileName, pec, pvRef, dwFlags);
+    
     return DI_OK;
 }
 
@@ -816,8 +1254,8 @@ HRESULT WINAPI IDirectInputDevice7WImpl_EnumEffectsInFile(LPDIRECTINPUTDEVICE8W
                                                          LPVOID pvRef,
                                                          DWORD dwFlags)
 {
-    FIXME("(%p)->(%s,%p,%p,%08lx): stub !\n", iface, debugstr_w(lpszFileName), pec, pvRef, dwFlags);
-
+    FIXME("(%p)->(%s,%p,%p,%08x): stub !\n", iface, debugstr_w(lpszFileName), pec, pvRef, dwFlags);
+    
     return DI_OK;
 }
 
@@ -827,8 +1265,8 @@ HRESULT WINAPI IDirectInputDevice7AImpl_WriteEffectToFile(LPDIRECTINPUTDEVICE8A
                                                          LPDIFILEEFFECT rgDiFileEft,
                                                          DWORD dwFlags)
 {
-    FIXME("(%p)->(%s,%08lx,%p,%08lx): stub !\n", iface, lpszFileName, dwEntries, rgDiFileEft, dwFlags);
-
+    FIXME("(%p)->(%s,%08x,%p,%08x): stub !\n", iface, lpszFileName, dwEntries, rgDiFileEft, dwFlags);
+    
     return DI_OK;
 }
 
@@ -838,8 +1276,8 @@ HRESULT WINAPI IDirectInputDevice7WImpl_WriteEffectToFile(LPDIRECTINPUTDEVICE8W
                                                          LPDIFILEEFFECT rgDiFileEft,
                                                          DWORD dwFlags)
 {
-    FIXME("(%p)->(%s,%08lx,%p,%08lx): stub !\n", iface, debugstr_w(lpszFileName), dwEntries, rgDiFileEft, dwFlags);
-
+    FIXME("(%p)->(%s,%08x,%p,%08x): stub !\n", iface, debugstr_w(lpszFileName), dwEntries, rgDiFileEft, dwFlags);
+    
     return DI_OK;
 }
 
@@ -848,8 +1286,14 @@ HRESULT WINAPI IDirectInputDevice8AImpl_BuildActionMap(LPDIRECTINPUTDEVICE8A ifa
                                                       LPCSTR lpszUserName,
                                                       DWORD dwFlags)
 {
-    FIXME("(%p)->(%p,%s,%08lx): stub !\n", iface, lpdiaf, lpszUserName, dwFlags);
-
+    FIXME("(%p)->(%p,%s,%08x): stub !\n", iface, lpdiaf, lpszUserName, dwFlags);
+#define X(x) if (dwFlags & x) FIXME("\tdwFlags =|"#x"\n");
+       X(DIDBAM_DEFAULT)
+       X(DIDBAM_PRESERVE)
+       X(DIDBAM_INITIALIZE)
+       X(DIDBAM_HWDEFAULTS)
+#undef X
+    _dump_diactionformatA(lpdiaf);
     return DI_OK;
 }
 
@@ -858,8 +1302,14 @@ HRESULT WINAPI IDirectInputDevice8WImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W ifa
                                                       LPCWSTR lpszUserName,
                                                       DWORD dwFlags)
 {
-    FIXME("(%p)->(%p,%s,%08lx): stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
-
+    FIXME("(%p)->(%p,%s,%08x): stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
+#define X(x) if (dwFlags & x) FIXME("\tdwFlags =|"#x"\n");
+       X(DIDBAM_DEFAULT)
+       X(DIDBAM_PRESERVE)
+       X(DIDBAM_INITIALIZE)
+       X(DIDBAM_HWDEFAULTS)
+#undef X
+  
     return DI_OK;
 }
 
@@ -868,8 +1318,8 @@ HRESULT WINAPI IDirectInputDevice8AImpl_SetActionMap(LPDIRECTINPUTDEVICE8A iface
                                                     LPCSTR lpszUserName,
                                                     DWORD dwFlags)
 {
-    FIXME("(%p)->(%p,%s,%08lx): stub !\n", iface, lpdiaf, lpszUserName, dwFlags);
-
+    FIXME("(%p)->(%p,%s,%08x): stub !\n", iface, lpdiaf, lpszUserName, dwFlags);
+    
     return DI_OK;
 }
 
@@ -878,8 +1328,8 @@ HRESULT WINAPI IDirectInputDevice8WImpl_SetActionMap(LPDIRECTINPUTDEVICE8W iface
                                                     LPCWSTR lpszUserName,
                                                     DWORD dwFlags)
 {
-    FIXME("(%p)->(%p,%s,%08lx): stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
-
+    FIXME("(%p)->(%p,%s,%08x): stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
+    
     return DI_OK;
 }
 
@@ -887,7 +1337,7 @@ HRESULT WINAPI IDirectInputDevice8AImpl_GetImageInfo(LPDIRECTINPUTDEVICE8A iface
                                                     LPDIDEVICEIMAGEINFOHEADERA lpdiDevImageInfoHeader)
 {
     FIXME("(%p)->(%p): stub !\n", iface, lpdiDevImageInfoHeader);
-
+    
     return DI_OK;
 }
 
@@ -895,6 +1345,6 @@ HRESULT WINAPI IDirectInputDevice8WImpl_GetImageInfo(LPDIRECTINPUTDEVICE8W iface
                                                     LPDIDEVICEIMAGEINFOHEADERW lpdiDevImageInfoHeader)
 {
     FIXME("(%p)->(%p): stub !\n", iface, lpdiDevImageInfoHeader);
-
+    
     return DI_OK;
 }
index 4567c85..3463382 100644 (file)
@@ -14,7 +14,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #ifndef __WINE_DLLS_DINPUT_DINPUTDEVICE_PRIVATE_H
 #include "windef.h"
 #include "winbase.h"
 #include "dinput.h"
+#include "wine/list.h"
+#include "dinput_private.h"
+
+typedef struct
+{
+    int size;
+    int offset_in;
+    int offset_out;
+    int value;
+} DataTransform;
+
+typedef struct
+{
+    int                         size;
+    int                         internal_format_size;
+    DataTransform              *dt;
+
+    int                        *offsets;     /* object offsets */
+    LPDIDATAFORMAT              wine_df;     /* wine internal data format */
+    LPDIDATAFORMAT              user_df;     /* user defined data format */
+} DataFormat;
 
 /* Device implementation */
 typedef struct IDirectInputDevice2AImpl IDirectInputDevice2AImpl;
 struct IDirectInputDevice2AImpl
 {
-        const IDirectInputDevice2AVtbl *lpVtbl;
-        LONG                            ref;
-        GUID                            guid;
+    const void                 *lpVtbl;
+    LONG                        ref;
+    GUID                        guid;
+    CRITICAL_SECTION            crit;
+    IDirectInputImpl           *dinput;
+    struct list                 entry;       /* entry into IDirectInput devices list */
+    HANDLE                      hEvent;
+    DWORD                       dwCoopLevel;
+    HWND                        win;
+    int                         acquired;
+    DI_EVENT_PROC               event_proc;  /* function to receive mouse & keyboard events */
+
+    LPDIDEVICEOBJECTDATA        data_queue;  /* buffer for 'GetDeviceData'.                 */
+    int                         queue_len;   /* size of the queue - set in 'SetProperty'    */
+    int                         queue_head;  /* position to write new event into queue      */
+    int                         queue_tail;  /* next event to read from queue               */
+    BOOL                        overflow;    /* return DI_BUFFEROVERFLOW in 'GetDeviceData' */
+
+    DataFormat                  data_format; /* user data format and wine to user format converter */
 };
 
-/* Routines to do DataFormat / WineFormat conversions */
-typedef struct {
-  int size;
-  int offset_in;
-  int offset_out;
-  int value;
-} DataTransform;
+extern BOOL get_app_key(HKEY*, HKEY*);
+extern DWORD get_config_key(HKEY, HKEY, const char*, char*, DWORD);
 
-typedef struct {
-  int size;
-  int internal_format_size;
-  DataTransform *dt;
-} DataFormat;
-extern void fill_DataFormat(void *out, const void *in, DataFormat *df) ;
-extern DataFormat *create_DataFormat(const DIDATAFORMAT *wine_format, LPCDIDATAFORMAT asked_format, int *offset) ;
+/* Routines to do DataFormat / WineFormat conversions */
+extern void fill_DataFormat(void *out, DWORD size, const void *in, const DataFormat *df) ;
 extern void release_DataFormat(DataFormat *df) ;
+extern void queue_event(LPDIRECTINPUTDEVICE8A iface, int ofs, DWORD data, DWORD time, DWORD seq);
+/* Helper functions to work with data format */
+extern int id_to_object(LPCDIDATAFORMAT df, int id);
+extern int id_to_offset(const DataFormat *df, int id);
+extern int find_property(const DataFormat *df, LPCDIPROPHEADER ph);
 
-/* Used to fill events in the queue */
-#define GEN_EVENT(offset,data,xtime,seq)                                       \
-{                                                                              \
-  /* If queue_len > 0, queuing is requested -> TRACE the event queued */       \
-  if (This->queue_len > 0) {                                                   \
-    int nq;                                                                    \
-    TRACE(" queueing %d at offset %d (queue head %d / size %d)\n",             \
-         (int) (data), (int) (offset),                                         \
-         (int) (This->queue_head), (int) (This->queue_len));                   \
-                                                                               \
-    nq = This->queue_head+1;                                                   \
-    while (nq >= This->queue_len) nq -= This->queue_len;                       \
-    if ((offset >= 0) && (nq != This->queue_tail)) {                           \
-      This->data_queue[This->queue_head].dwOfs = offset;                       \
-      This->data_queue[This->queue_head].dwData = data;                                \
-      This->data_queue[This->queue_head].dwTimeStamp = xtime;                  \
-      This->data_queue[This->queue_head].dwSequence = seq;                     \
-      This->queue_head = nq;                                                   \
-    } else                                                                      \
-      This->overflow = TRUE;                                                    \
-  }                                                                            \
-}
-
-/**
- * Callback Data used by specific callback
- *  for EnumObject on 'W' interfaces
- */
-typedef struct {
-  LPDIENUMDEVICEOBJECTSCALLBACKW lpCallBack;
-  LPVOID lpvRef;
-} device_enumobjects_AtoWcb_data;
+/* Common joystick stuff */
+typedef struct
+{
+    LONG lDevMin;
+    LONG lDevMax;
+    LONG lMin;
+    LONG lMax;
+    LONG lDeadZone;
+    LONG lSaturation;
+} ObjProps;
+
+extern DWORD joystick_map_pov(POINTL *p);
+extern LONG joystick_map_axis(ObjProps *props, int val);
 
-extern BOOL DIEnumDevicesCallbackAtoW(LPCDIDEVICEOBJECTINSTANCEA, LPVOID);
+typedef struct
+{
+    struct list entry;
+    LPDIRECTINPUTEFFECT ref;
+} effect_list_item;
 
+extern const GUID DInput_Wine_Keyboard_GUID;
+extern const GUID DInput_Wine_Mouse_GUID;
 
 /* Various debug tools */
-extern void _dump_cooperativelevel_DI(DWORD dwFlags) ;
-extern void _dump_EnumObjects_flags(DWORD dwFlags) ;
 extern void _dump_DIPROPHEADER(LPCDIPROPHEADER diph) ;
-extern void _dump_OBJECTINSTANCEA(DIDEVICEOBJECTINSTANCEA *ddoi) ;
-extern void _dump_OBJECTINSTANCEW(DIDEVICEOBJECTINSTANCEW *ddoi) ;
+extern void _dump_OBJECTINSTANCEA(const DIDEVICEOBJECTINSTANCEA *ddoi) ;
+extern void _dump_OBJECTINSTANCEW(const DIDEVICEOBJECTINSTANCEW *ddoi) ;
 extern void _dump_DIDATAFORMAT(const DIDATAFORMAT *df) ;
 extern const char *_dump_dinput_GUID(const GUID *guid) ;
 
 /* And the stubs */
+extern HRESULT WINAPI IDirectInputDevice2AImpl_Acquire(LPDIRECTINPUTDEVICE8A iface);
+extern HRESULT WINAPI IDirectInputDevice2AImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface);
 extern HRESULT WINAPI IDirectInputDevice2AImpl_SetDataFormat(
        LPDIRECTINPUTDEVICE8A iface,LPCDIDATAFORMAT df ) ;
 extern HRESULT WINAPI IDirectInputDevice2AImpl_SetCooperativeLevel(
@@ -118,25 +138,19 @@ extern HRESULT WINAPI IDirectInputDevice2WImpl_EnumObjects(
        LPDIENUMDEVICEOBJECTSCALLBACKW lpCallback,
        LPVOID lpvRef,
        DWORD dwFlags) ;
-extern HRESULT WINAPI IDirectInputDevice2AImpl_GetProperty(
-       LPDIRECTINPUTDEVICE8A iface,
-       REFGUID rguid,
-       LPDIPROPHEADER pdiph) ;
+extern HRESULT WINAPI IDirectInputDevice2AImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPDIPROPHEADER pdiph);
+extern HRESULT WINAPI IDirectInputDevice2AImpl_SetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPCDIPROPHEADER pdiph);
 extern HRESULT WINAPI IDirectInputDevice2AImpl_GetObjectInfo(
        LPDIRECTINPUTDEVICE8A iface,
        LPDIDEVICEOBJECTINSTANCEA pdidoi,
        DWORD dwObj,
        DWORD dwHow) ;
-extern HRESULT WINAPI IDirectInputDevice2WImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8W iface,
+extern HRESULT WINAPI IDirectInputDevice2WImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8W iface, 
                                                             LPDIDEVICEOBJECTINSTANCEW pdidoi,
                                                             DWORD dwObj,
                                                             DWORD dwHow);
-extern HRESULT WINAPI IDirectInputDevice2AImpl_GetDeviceInfo(
-       LPDIRECTINPUTDEVICE8A iface,
-       LPDIDEVICEINSTANCEA pdidi) ;
-extern HRESULT WINAPI IDirectInputDevice2WImpl_GetDeviceInfo(
-       LPDIRECTINPUTDEVICE8W iface,
-       LPDIDEVICEINSTANCEW pdidi) ;
+extern HRESULT WINAPI IDirectInputDevice2AImpl_GetDeviceData(LPDIRECTINPUTDEVICE8A iface,
+        DWORD dodsize, LPDIDEVICEOBJECTDATA dod, LPDWORD entries, DWORD flags);
 extern HRESULT WINAPI IDirectInputDevice2AImpl_RunControlPanel(
        LPDIRECTINPUTDEVICE8A iface,
        HWND hwndOwner,
index bca3429..127853b 100644 (file)
@@ -3,6 +3,7 @@
 <module name="dinput" type="win32dll" baseaddress="${BASEADDRESS_DINPUT}" installbase="system32" installname="dinput.dll" unicode="yes">
        <autoregister infsection="OleControlDlls" type="DllRegisterServer" />
        <importlibrary definition="dinput.spec" />
+       <define name="_WIN32_WINNT">0x600</define>
        <include base="dinput">.</include>
        <include base="ReactOS">include/reactos/wine</include>
        <library>wine</library>
index bdb3d2f..46e4e28 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright 1998 Marcus Meissner
  * Copyright 1998,1999 Lionel Ulmer
  * Copyright 2000-2002 TransGaming Technologies Inc.
+ * Copyright 2007 Vitaliy Margolen
  *
  *
  * This library is free software; you can redistribute it and/or
@@ -17,7 +18,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 /* Status:
  *
@@ -35,6 +36,7 @@
 #include <string.h>
 
 #define COBJMACROS
+#define NONAMELESSUNION
 
 #include "wine/debug.h"
 #include "wine/unicode.h"
@@ -43,6 +45,7 @@
 #include "winuser.h"
 #include "winerror.h"
 #include "dinput_private.h"
+#include "device_private.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
 
@@ -51,6 +54,26 @@ static const IDirectInput7WVtbl ddi7wvt;
 static const IDirectInput8AVtbl ddi8avt;
 static const IDirectInput8WVtbl ddi8wvt;
 
+static inline IDirectInputImpl *impl_from_IDirectInput7W( IDirectInput7W *iface )
+{
+    return CONTAINING_RECORD( iface, IDirectInputImpl, lpVtbl7w );
+}
+
+static inline IDirectInputImpl *impl_from_IDirectInput8A( IDirectInput8A *iface )
+{
+    return CONTAINING_RECORD( iface, IDirectInputImpl, lpVtbl8a );
+}
+
+static inline IDirectInputImpl *impl_from_IDirectInput8W( IDirectInput8W *iface )
+{
+    return CONTAINING_RECORD( iface, IDirectInputImpl, lpVtbl8w );
+}
+
+static inline IDirectInput7W *IDirectInput7W_from_impl( IDirectInputImpl *iface )
+{
+    return (IDirectInput7W *)(&iface->lpVtbl7w);
+}
+
 static const struct dinput_device *dinput_devices[] =
 {
     &mouse_device,
@@ -60,7 +83,7 @@ static const struct dinput_device *dinput_devices[] =
 };
 #define NB_DINPUT_DEVICES (sizeof(dinput_devices)/sizeof(dinput_devices[0]))
 
-HINSTANCE DINPUT_instance = NULL;
+static HINSTANCE DINPUT_instance = NULL;
 
 BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserv)
 {
@@ -76,63 +99,63 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserv)
     return TRUE;
 }
 
+static BOOL check_hook_thread(void);
+static CRITICAL_SECTION dinput_hook_crit;
+static struct list direct_input_list = LIST_INIT( direct_input_list );
 
 /******************************************************************************
  *     DirectInputCreateEx (DINPUT.@)
  */
 HRESULT WINAPI DirectInputCreateEx(
        HINSTANCE hinst, DWORD dwVersion, REFIID riid, LPVOID *ppDI,
-       LPUNKNOWN punkOuter)
+       LPUNKNOWN punkOuter) 
 {
-       IDirectInputImpl* This;
-
-       TRACE("(%p,%04lx,%s,%p,%p)\n", hinst,dwVersion,debugstr_guid(riid),ppDI,punkOuter);
-
-       if (IsEqualGUID(&IID_IDirectInputA,riid) ||
-           IsEqualGUID(&IID_IDirectInput2A,riid) ||
-           IsEqualGUID(&IID_IDirectInput7A,riid)) {
-         This = HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInputImpl));
-         This->lpVtbl = &ddi7avt;
-         This->ref = 1;
-         This->dwVersion = dwVersion;
-         *ppDI = This;
-
-         return DI_OK;
-       }
-
-       if (IsEqualGUID(&IID_IDirectInputW,riid) ||
-           IsEqualGUID(&IID_IDirectInput2W,riid) ||
-           IsEqualGUID(&IID_IDirectInput7W,riid)) {
-         This = HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInputImpl));
-         This->lpVtbl = &ddi7wvt;
-         This->ref = 1;
-         This->dwVersion = dwVersion;
-         *ppDI = This;
+    IDirectInputImpl* This;
+
+    TRACE("(%p,%04x,%s,%p,%p)\n", hinst, dwVersion, debugstr_guid(riid), ppDI, punkOuter);
+
+    if (IsEqualGUID( &IID_IUnknown,       riid ) ||
+        IsEqualGUID( &IID_IDirectInputA,  riid ) ||
+        IsEqualGUID( &IID_IDirectInput2A, riid ) ||
+        IsEqualGUID( &IID_IDirectInput7A, riid ) ||
+        IsEqualGUID( &IID_IDirectInputW,  riid ) ||
+        IsEqualGUID( &IID_IDirectInput2W, riid ) ||
+        IsEqualGUID( &IID_IDirectInput7W, riid ) ||
+        IsEqualGUID( &IID_IDirectInput8A, riid ) ||
+        IsEqualGUID( &IID_IDirectInput8W, riid ))
+    {
+        if (!(This = HeapAlloc( GetProcessHeap(), 0, sizeof(IDirectInputImpl) )))
+            return DIERR_OUTOFMEMORY;
+    }
+    else
+        return DIERR_OLDDIRECTINPUTVERSION;
 
-         return DI_OK;
-       }
+    This->lpVtbl      = &ddi7avt;
+    This->lpVtbl7w    = &ddi7wvt;
+    This->lpVtbl8a    = &ddi8avt;
+    This->lpVtbl8w    = &ddi8wvt;
+    This->ref         = 0;
+    This->dwVersion   = dwVersion;
+    This->evsequence  = 1;
 
-       if (IsEqualGUID(&IID_IDirectInput8A,riid)) {
-         This = HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInputImpl));
-         This->lpVtbl = &ddi8avt;
-         This->ref = 1;
-         This->dwVersion = dwVersion;
-         *ppDI = This;
+    InitializeCriticalSection(&This->crit);
+    This->crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IDirectInputImpl*->crit");
 
-         return DI_OK;
-       }
+    list_init( &This->devices_list );
 
-       if (IsEqualGUID(&IID_IDirectInput8W,riid)) {
-         This = HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInputImpl));
-         This->lpVtbl = &ddi8wvt;
-         This->ref = 1;
-         This->dwVersion = dwVersion;
-         *ppDI = This;
+    /* Add self to the list of the IDirectInputs */
+    EnterCriticalSection( &dinput_hook_crit );
+    list_add_head( &direct_input_list, &This->entry );
+    LeaveCriticalSection( &dinput_hook_crit );
 
-         return DI_OK;
-       }
+    if (!check_hook_thread())
+    {
+        IUnknown_Release( (LPDIRECTINPUT7A)This );
+        return DIERR_GENERIC;
+    }
 
-       return DIERR_OLDDIRECTINPUTVERSION;
+    IDirectInput_QueryInterface( (IDirectInput7A *)This, riid, ppDI );
+    return DI_OK;
 }
 
 /******************************************************************************
@@ -140,15 +163,7 @@ HRESULT WINAPI DirectInputCreateEx(
  */
 HRESULT WINAPI DirectInputCreateA(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTA *ppDI, LPUNKNOWN punkOuter)
 {
-       IDirectInputImpl* This;
-       TRACE("(0x%08lx,%04lx,%p,%p)\n", (DWORD)hinst,dwVersion,ppDI,punkOuter);
-       This = HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInputImpl));
-       This->lpVtbl = &ddi7avt;
-       This->ref = 1;
-       This->dwVersion = dwVersion;
-       *ppDI = (IDirectInputA*)This;
-       return 0;
-
+    return DirectInputCreateEx(hinst, dwVersion, &IID_IDirectInput7A, (LPVOID *)ppDI, punkOuter);
 }
 
 /******************************************************************************
@@ -156,14 +171,7 @@ HRESULT WINAPI DirectInputCreateA(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPU
  */
 HRESULT WINAPI DirectInputCreateW(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTW *ppDI, LPUNKNOWN punkOuter)
 {
-       IDirectInputImpl* This;
-       TRACE("(0x%08lx,%04lx,%p,%p)\n", (DWORD)hinst,dwVersion,ppDI,punkOuter);
-       This = HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInputImpl));
-       This->lpVtbl = &ddi7wvt;
-       This->ref = 1;
-       This->dwVersion = dwVersion;
-       *ppDI = (IDirectInputW*)This;
-       return 0;
+    return DirectInputCreateEx(hinst, dwVersion, &IID_IDirectInput7W, (LPVOID *)ppDI, punkOuter);
 }
 
 static const char *_dump_DIDEVTYPE_value(DWORD dwDevType) {
@@ -192,14 +200,45 @@ static void _dump_EnumDevices_dwFlags(DWORD dwFlags) {
            FE(DIEDFL_INCLUDEPHANTOMS)
 #undef FE
        };
+       TRACE(" flags: ");
        if (dwFlags == 0) {
-           DPRINTF("DIEDFL_ALLDEVICES");
+           TRACE("DIEDFL_ALLDEVICES");
            return;
        }
        for (i = 0; i < (sizeof(flags) / sizeof(flags[0])); i++)
            if (flags[i].mask & dwFlags)
-               DPRINTF("%s ",flags[i].name);
+               TRACE("%s ",flags[i].name);
+    }
+    TRACE("\n");
+}
+
+void _dump_diactionformatA(LPDIACTIONFORMATA lpdiActionFormat) {
+    unsigned int i;
+
+    FIXME("diaf.dwSize = %d\n", lpdiActionFormat->dwSize);
+    FIXME("diaf.dwActionSize = %d\n", lpdiActionFormat->dwActionSize);
+    FIXME("diaf.dwDataSize = %d\n", lpdiActionFormat->dwDataSize);
+    FIXME("diaf.dwNumActions = %d\n", lpdiActionFormat->dwNumActions);
+    FIXME("diaf.rgoAction = %p\n", lpdiActionFormat->rgoAction);
+    for (i=0;i<lpdiActionFormat->dwNumActions;i++) {
+        FIXME("diaf.rgoAction[%u]:\n", i);
+        FIXME("\tuAppData=%lx\n", lpdiActionFormat->rgoAction[i].uAppData);
+        FIXME("\tdwSemantics=%x\n", lpdiActionFormat->rgoAction[i].dwSemantics);
+        FIXME("\tdwFlags=%x\n", lpdiActionFormat->rgoAction[i].dwFlags);
+        FIXME("\tszActionName=%s\n", debugstr_a(lpdiActionFormat->rgoAction[i].u.lptszActionName));
+        FIXME("\tguidInstance=%s\n", debugstr_guid(&lpdiActionFormat->rgoAction[i].guidInstance));
+        FIXME("\tdwObjID=%x\n", lpdiActionFormat->rgoAction[i].dwObjID);
+        FIXME("\tdwHow=%x\n", lpdiActionFormat->rgoAction[i].dwHow);
     }
+    FIXME("diaf.guidActionMap = %s\n", debugstr_guid(&lpdiActionFormat->guidActionMap));
+    FIXME("diaf.dwGenre = %d\n", lpdiActionFormat->dwGenre);
+    FIXME("diaf.dwBufferSize = %d\n", lpdiActionFormat->dwBufferSize);
+    FIXME("diaf.lAxisMin = %d\n", lpdiActionFormat->lAxisMin);
+    FIXME("diaf.lAxisMax = %d\n", lpdiActionFormat->lAxisMax);
+    FIXME("diaf.hInstString = %p\n", lpdiActionFormat->hInstString);
+    FIXME("diaf.ftTimeStamp ...\n");
+    FIXME("diaf.dwCRC = %x\n", lpdiActionFormat->dwCRC);
+    FIXME("diaf.tszActionMap = %s\n", debugstr_a(lpdiActionFormat->tszActionMap));
 }
 
 /******************************************************************************
@@ -211,25 +250,26 @@ static HRESULT WINAPI IDirectInputAImpl_EnumDevices(
 {
     IDirectInputImpl *This = (IDirectInputImpl *)iface;
     DIDEVICEINSTANCEA devInstance;
-    int i, j, r;
+    unsigned int i;
+    int j, r;
 
-    TRACE("(this=%p,0x%04lx '%s',%p,%p,%04lx)\n",
+    TRACE("(this=%p,0x%04x '%s',%p,%p,%04x)\n",
          This, dwDevType, _dump_DIDEVTYPE_value(dwDevType),
          lpCallback, pvRef, dwFlags);
-    TRACE(" flags: "); _dump_EnumDevices_dwFlags(dwFlags); TRACE("\n");
+    _dump_EnumDevices_dwFlags(dwFlags);
 
     for (i = 0; i < NB_DINPUT_DEVICES; i++) {
         if (!dinput_devices[i]->enum_deviceA) continue;
         for (j = 0, r = -1; r != 0; j++) {
            devInstance.dwSize = sizeof(devInstance);
-           TRACE("  - checking device %d ('%s')\n", i, dinput_devices[i]->name);
+           TRACE("  - checking device %u ('%s')\n", i, dinput_devices[i]->name);
            if ((r = dinput_devices[i]->enum_deviceA(dwDevType, dwFlags, &devInstance, This->dwVersion, j))) {
                if (lpCallback(&devInstance,pvRef) == DIENUM_STOP)
                    return 0;
            }
        }
     }
-
+    
     return 0;
 }
 /******************************************************************************
@@ -237,172 +277,203 @@ static HRESULT WINAPI IDirectInputAImpl_EnumDevices(
  */
 static HRESULT WINAPI IDirectInputWImpl_EnumDevices(
        LPDIRECTINPUT7W iface, DWORD dwDevType, LPDIENUMDEVICESCALLBACKW lpCallback,
-       LPVOID pvRef, DWORD dwFlags)
+       LPVOID pvRef, DWORD dwFlags) 
 {
-    IDirectInputImpl *This = (IDirectInputImpl *)iface;
+    IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
     DIDEVICEINSTANCEW devInstance;
-    int i, j, r;
+    unsigned int i;
+    int j, r;
 
-    TRACE("(this=%p,0x%04lx '%s',%p,%p,%04lx)\n",
+    TRACE("(this=%p,0x%04x '%s',%p,%p,%04x)\n",
          This, dwDevType, _dump_DIDEVTYPE_value(dwDevType),
          lpCallback, pvRef, dwFlags);
-    TRACE(" flags: "); _dump_EnumDevices_dwFlags(dwFlags); TRACE("\n");
+    _dump_EnumDevices_dwFlags(dwFlags);
 
     for (i = 0; i < NB_DINPUT_DEVICES; i++) {
         if (!dinput_devices[i]->enum_deviceW) continue;
         for (j = 0, r = -1; r != 0; j++) {
            devInstance.dwSize = sizeof(devInstance);
-           TRACE("  - checking device %d ('%s')\n", i, dinput_devices[i]->name);
+           TRACE("  - checking device %u ('%s')\n", i, dinput_devices[i]->name);
            if ((r = dinput_devices[i]->enum_deviceW(dwDevType, dwFlags, &devInstance, This->dwVersion, j))) {
                if (lpCallback(&devInstance,pvRef) == DIENUM_STOP)
                    return 0;
            }
        }
     }
-
+    
     return 0;
 }
 
 static ULONG WINAPI IDirectInputAImpl_AddRef(LPDIRECTINPUT7A iface)
 {
-       IDirectInputImpl *This = (IDirectInputImpl *)iface;
-       return InterlockedIncrement((&This->ref));
+    IDirectInputImpl *This = (IDirectInputImpl *)iface;
+    ULONG ref = InterlockedIncrement(&This->ref);
+
+    TRACE( "(%p) incrementing from %d\n", This, ref - 1);
+    return ref;
 }
 
-static ULONG WINAPI IDirectInputAImpl_Release(LPDIRECTINPUT7A iface)
+static ULONG WINAPI IDirectInputWImpl_AddRef(LPDIRECTINPUT7W iface)
 {
-       IDirectInputImpl *This = (IDirectInputImpl *)iface;
-       ULONG ref;
-       ref = InterlockedDecrement(&(This->ref));
-       if (ref == 0)
-               HeapFree(GetProcessHeap(),0,This);
-       return ref;
-}
-
-static HRESULT WINAPI IDirectInputAImpl_QueryInterface(LPDIRECTINPUT7A iface, REFIID riid, LPVOID *ppobj) {
-       IDirectInputImpl *This = (IDirectInputImpl *)iface;
-
-       TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
-       if (IsEqualGUID(&IID_IUnknown,riid) ||
-           IsEqualGUID(&IID_IDirectInputA,riid) ||
-           IsEqualGUID(&IID_IDirectInput2A,riid) ||
-           IsEqualGUID(&IID_IDirectInput7A,riid)) {
-               IDirectInputAImpl_AddRef(iface);
-               *ppobj = This;
-               return 0;
-       }
-       TRACE("Unsupported interface !\n");
-       return E_FAIL;
+    IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
+    return IDirectInputAImpl_AddRef( (IDirectInput7A *)This );
 }
 
-static HRESULT WINAPI IDirectInputWImpl_QueryInterface(LPDIRECTINPUT7W iface, REFIID riid, LPVOID *ppobj) {
-       IDirectInputImpl *This = (IDirectInputImpl *)iface;
+static ULONG WINAPI IDirectInputAImpl_Release(LPDIRECTINPUT7A iface)
+{
+    IDirectInputImpl *This = (IDirectInputImpl *)iface;
+    ULONG ref = InterlockedDecrement( &This->ref );
 
-       TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
-       if (IsEqualGUID(&IID_IUnknown,riid) ||
-           IsEqualGUID(&IID_IDirectInputW,riid) ||
-           IsEqualGUID(&IID_IDirectInput2W,riid) ||
-           IsEqualGUID(&IID_IDirectInput7W,riid)) {
-               IDirectInputAImpl_AddRef((LPDIRECTINPUT7A) iface);
-               *ppobj = This;
-               return 0;
-       }
-       TRACE("Unsupported interface !\n");
-       return E_FAIL;
+    TRACE( "(%p) releasing from %d\n", This, ref + 1 );
+
+    if (ref) return ref;
+
+    /* Remove self from the list of the IDirectInputs */
+    EnterCriticalSection( &dinput_hook_crit );
+    list_remove( &This->entry );
+    LeaveCriticalSection( &dinput_hook_crit );
+
+    check_hook_thread();
+
+    This->crit.DebugInfo->Spare[0] = 0;
+    DeleteCriticalSection( &This->crit );
+    HeapFree( GetProcessHeap(), 0, This );
+
+    return 0;
 }
 
-static HRESULT WINAPI IDirectInputAImpl_CreateDevice(
-       LPDIRECTINPUT7A iface,REFGUID rguid,LPDIRECTINPUTDEVICEA* pdev,
-       LPUNKNOWN punk
-) {
-       IDirectInputImpl *This = (IDirectInputImpl *)iface;
-       HRESULT ret_value = DIERR_DEVICENOTREG;
-       int i;
+static ULONG WINAPI IDirectInputWImpl_Release(LPDIRECTINPUT7W iface)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
+    return IDirectInputAImpl_Release( (IDirectInput7A *)This );
+}
 
-       TRACE("(this=%p,%s,%p,%p)\n",This,debugstr_guid(rguid),pdev,punk);
+static HRESULT WINAPI IDirectInputAImpl_QueryInterface(LPDIRECTINPUT7A iface, REFIID riid, LPVOID *ppobj)
+{
+    IDirectInputImpl *This = (IDirectInputImpl *)iface;
 
-       if (pdev == NULL) {
-               WARN("invalid pointer: pdev == NULL\n");
-               return E_POINTER;
-       }
+    TRACE( "(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppobj );
 
-       if (rguid == NULL) {
-               WARN("invalid pointer: rguid == NULL\n");
-               return E_POINTER;
-       }
+    if (IsEqualGUID( &IID_IUnknown, riid ) ||
+        IsEqualGUID( &IID_IDirectInputA,  riid ) ||
+        IsEqualGUID( &IID_IDirectInput2A, riid ) ||
+        IsEqualGUID( &IID_IDirectInput7A, riid ))
+    {
+        *ppobj = &This->lpVtbl;
+        IUnknown_AddRef( (IUnknown*)*ppobj );
 
-       /* Loop on all the devices to see if anyone matches the given GUID */
-       for (i = 0; i < NB_DINPUT_DEVICES; i++) {
-         HRESULT ret;
-         if (!dinput_devices[i]->create_deviceA) continue;
-         if ((ret = dinput_devices[i]->create_deviceA(This, rguid, NULL, pdev)) == DI_OK)
-           return DI_OK;
+        return DI_OK;
+    }
 
-         if (ret == DIERR_NOINTERFACE)
-           ret_value = DIERR_NOINTERFACE;
-       }
+    if (IsEqualGUID( &IID_IDirectInputW,  riid ) ||
+        IsEqualGUID( &IID_IDirectInput2W, riid ) ||
+        IsEqualGUID( &IID_IDirectInput7W, riid ))
+    {
+        *ppobj = &This->lpVtbl7w;
+        IUnknown_AddRef( (IUnknown*)*ppobj );
 
-       return ret_value;
-}
+        return DI_OK;
+    }
 
-static HRESULT WINAPI IDirectInputWImpl_CreateDevice(LPDIRECTINPUT7A iface,
-                                                    REFGUID rguid, LPDIRECTINPUTDEVICEW* pdev, LPUNKNOWN punk) {
-        IDirectInputImpl *This = (IDirectInputImpl *)iface;
-       HRESULT ret_value = DIERR_DEVICENOTREG;
-       int i;
+    if (IsEqualGUID( &IID_IDirectInput8A, riid ))
+    {
+        *ppobj = &This->lpVtbl8a;
+        IUnknown_AddRef( (IUnknown*)*ppobj );
 
-       TRACE("(this=%p,%s,%p,%p)\n",This,debugstr_guid(rguid),pdev,punk);
+        return DI_OK;
+    }
 
-       /* Loop on all the devices to see if anyone matches the given GUID */
-       for (i = 0; i < NB_DINPUT_DEVICES; i++) {
-         HRESULT ret;
-         if (!dinput_devices[i]->create_deviceW) continue;
-         if ((ret = dinput_devices[i]->create_deviceW(This, rguid, NULL, pdev)) == DI_OK)
-           return DI_OK;
+    if (IsEqualGUID( &IID_IDirectInput8W, riid ))
+    {
+        *ppobj = &This->lpVtbl8w;
+        IUnknown_AddRef( (IUnknown*)*ppobj );
 
-         if (ret == DIERR_NOINTERFACE)
-           ret_value = DIERR_NOINTERFACE;
-       }
+        return DI_OK;
+    }
+
+    FIXME( "Unsupported interface !\n" );
+    return E_FAIL;
+}
 
-       return ret_value;
+static HRESULT WINAPI IDirectInputWImpl_QueryInterface(LPDIRECTINPUT7W iface, REFIID riid, LPVOID *ppobj)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
+    return IDirectInputAImpl_QueryInterface( (IDirectInput7A *)This, riid, ppobj );
 }
 
 static HRESULT WINAPI IDirectInputAImpl_Initialize(LPDIRECTINPUT7A iface, HINSTANCE hinst, DWORD x) {
-       return DIERR_ALREADYINITIALIZED;
+       TRACE("(this=%p,%p,%x)\n",iface, hinst, x);
+       
+       /* Initialize can return: DIERR_BETADIRECTINPUTVERSION, DIERR_OLDDIRECTINPUTVERSION and DI_OK.
+        * Since we already initialized the device, return DI_OK. In the past we returned DIERR_ALREADYINITIALIZED
+        * which broke applications like Tomb Raider Legend because it isn't a legal return value.
+        */
+       return DI_OK;
 }
 
-static HRESULT WINAPI IDirectInputAImpl_GetDeviceStatus(LPDIRECTINPUT7A iface,
-                                                       REFGUID rguid) {
-  IDirectInputImpl *This = (IDirectInputImpl *)iface;
+static HRESULT WINAPI IDirectInputWImpl_Initialize(LPDIRECTINPUT7W iface, HINSTANCE hinst, DWORD x)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
+    return IDirectInputAImpl_Initialize( (IDirectInput7A *)This, hinst, x );
+}
 
-  FIXME("(%p)->(%s): stub\n",This,debugstr_guid(rguid));
+static HRESULT WINAPI IDirectInputAImpl_GetDeviceStatus(LPDIRECTINPUT7A iface, REFGUID rguid)
+{
+    IDirectInputImpl *This = (IDirectInputImpl *)iface;
+    HRESULT hr;
+    LPDIRECTINPUTDEVICEA device;
+
+    TRACE( "(%p)->(%s)\n", This, debugstr_guid(rguid) );
+
+    hr = IDirectInput_CreateDevice( iface, rguid, &device, NULL );
+    if (hr != DI_OK) return DI_NOTATTACHED;
+
+    IUnknown_Release( device );
+
+    return DI_OK;
+}
 
-  return DI_OK;
+static HRESULT WINAPI IDirectInputWImpl_GetDeviceStatus(LPDIRECTINPUT7W iface, REFGUID rguid)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
+    return IDirectInputAImpl_GetDeviceStatus( (IDirectInput7A *)This, rguid );
 }
 
 static HRESULT WINAPI IDirectInputAImpl_RunControlPanel(LPDIRECTINPUT7A iface,
                                                        HWND hwndOwner,
-                                                       DWORD dwFlags) {
-  IDirectInputImpl *This = (IDirectInputImpl *)iface;
-  FIXME("(%p)->(%p,%08lx): stub\n",This, hwndOwner, dwFlags);
+                                                       DWORD dwFlags)
+{
+    IDirectInputImpl *This = (IDirectInputImpl *)iface;
+
+    FIXME( "(%p)->(%p,%08x): stub\n", This, hwndOwner, dwFlags );
+
+    return DI_OK;
+}
 
-  return DI_OK;
+static HRESULT WINAPI IDirectInputWImpl_RunControlPanel(LPDIRECTINPUT7W iface, HWND hwndOwner, DWORD dwFlags)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
+    return IDirectInputAImpl_RunControlPanel( (IDirectInput7A *)This, hwndOwner, dwFlags );
 }
 
 static HRESULT WINAPI IDirectInput2AImpl_FindDevice(LPDIRECTINPUT7A iface, REFGUID rguid,
-                                                   LPCSTR pszName, LPGUID pguidInstance) {
-  IDirectInputImpl *This = (IDirectInputImpl *)iface;
-  FIXME("(%p)->(%s, %s, %p): stub\n", This, debugstr_guid(rguid), pszName, pguidInstance);
+                                                   LPCSTR pszName, LPGUID pguidInstance)
+{
+    IDirectInputImpl *This = (IDirectInputImpl *)iface;
 
-  return DI_OK;
+    FIXME( "(%p)->(%s, %s, %p): stub\n", This, debugstr_guid(rguid), pszName, pguidInstance );
+
+    return DI_OK;
 }
 
 static HRESULT WINAPI IDirectInput2WImpl_FindDevice(LPDIRECTINPUT7W iface, REFGUID rguid,
-                                                   LPCWSTR pszName, LPGUID pguidInstance) {
-  IDirectInputImpl *This = (IDirectInputImpl *)iface;
-  FIXME("(%p)->(%s, %s, %p): stub\n", This, debugstr_guid(rguid), debugstr_w(pszName), pguidInstance);
+                                                   LPCWSTR pszName, LPGUID pguidInstance)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
 
-  return DI_OK;
+    FIXME( "(%p)->(%s, %s, %p): stub\n", This, debugstr_guid(rguid), debugstr_w(pszName), pguidInstance );
+
+    return DI_OK;
 }
 
 static HRESULT WINAPI IDirectInput7AImpl_CreateDeviceEx(LPDIRECTINPUT7A iface, REFGUID rguid,
@@ -410,39 +481,60 @@ static HRESULT WINAPI IDirectInput7AImpl_CreateDeviceEx(LPDIRECTINPUT7A iface, R
 {
   IDirectInputImpl *This = (IDirectInputImpl *)iface;
   HRESULT ret_value = DIERR_DEVICENOTREG;
-  int i;
+  unsigned int i;
 
   TRACE("(%p)->(%s, %s, %p, %p)\n", This, debugstr_guid(rguid), debugstr_guid(riid), pvOut, lpUnknownOuter);
 
+  if (!rguid || !pvOut) return E_POINTER;
+
   /* Loop on all the devices to see if anyone matches the given GUID */
   for (i = 0; i < NB_DINPUT_DEVICES; i++) {
     HRESULT ret;
+
     if (!dinput_devices[i]->create_deviceA) continue;
     if ((ret = dinput_devices[i]->create_deviceA(This, rguid, riid, (LPDIRECTINPUTDEVICEA*) pvOut)) == DI_OK)
+    {
+      EnterCriticalSection( &This->crit );
+      list_add_tail( &This->devices_list, &(*(IDirectInputDevice2AImpl**)pvOut)->entry );
+      LeaveCriticalSection( &This->crit );
       return DI_OK;
+    }
 
     if (ret == DIERR_NOINTERFACE)
       ret_value = DIERR_NOINTERFACE;
   }
 
+  if (ret_value == DIERR_NOINTERFACE)
+  {
+    WARN("invalid device GUID %s\n", debugstr_guid(rguid));
+  }
+
   return ret_value;
 }
 
 static HRESULT WINAPI IDirectInput7WImpl_CreateDeviceEx(LPDIRECTINPUT7W iface, REFGUID rguid,
                                                        REFIID riid, LPVOID* pvOut, LPUNKNOWN lpUnknownOuter)
 {
-  IDirectInputImpl *This = (IDirectInputImpl *)iface;
+  IDirectInputImpl *This = impl_from_IDirectInput7W( iface );
   HRESULT ret_value = DIERR_DEVICENOTREG;
-  int i;
+  unsigned int i;
 
   TRACE("(%p)->(%s, %s, %p, %p)\n", This, debugstr_guid(rguid), debugstr_guid(riid), pvOut, lpUnknownOuter);
 
+  if (!rguid || !pvOut) return E_POINTER;
+
   /* Loop on all the devices to see if anyone matches the given GUID */
   for (i = 0; i < NB_DINPUT_DEVICES; i++) {
     HRESULT ret;
+
     if (!dinput_devices[i]->create_deviceW) continue;
     if ((ret = dinput_devices[i]->create_deviceW(This, rguid, riid, (LPDIRECTINPUTDEVICEW*) pvOut)) == DI_OK)
+    {
+      EnterCriticalSection( &This->crit );
+      list_add_tail( &This->devices_list, &(*(IDirectInputDevice2AImpl**)pvOut)->entry );
+      LeaveCriticalSection( &This->crit );
       return DI_OK;
+    }
 
     if (ret == DIERR_NOINTERFACE)
       ret_value = DIERR_NOINTERFACE;
@@ -451,32 +543,132 @@ static HRESULT WINAPI IDirectInput7WImpl_CreateDeviceEx(LPDIRECTINPUT7W iface, R
   return ret_value;
 }
 
-static HRESULT WINAPI IDirectInput8AImpl_QueryInterface(LPDIRECTINPUT8A iface, REFIID riid, LPVOID *ppobj) {
-      IDirectInputImpl *This = (IDirectInputImpl *)iface;
+static HRESULT WINAPI IDirectInputAImpl_CreateDevice(LPDIRECTINPUT7A iface, REFGUID rguid,
+                                                     LPDIRECTINPUTDEVICEA* pdev, LPUNKNOWN punk)
+{
+    return IDirectInput7AImpl_CreateDeviceEx(iface, rguid, NULL, (LPVOID*)pdev, punk);
+}
+
+static HRESULT WINAPI IDirectInputWImpl_CreateDevice(LPDIRECTINPUT7W iface, REFGUID rguid,
+                                                     LPDIRECTINPUTDEVICEW* pdev, LPUNKNOWN punk)
+{
+    return IDirectInput7WImpl_CreateDeviceEx(iface, rguid, NULL, (LPVOID*)pdev, punk);
+}
+
+/*******************************************************************************
+ *      DirectInput8
+ */
+
+static ULONG WINAPI IDirectInput8AImpl_AddRef(LPDIRECTINPUT8A iface)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
+    return IDirectInputAImpl_AddRef( (IDirectInput7A *)This );
+}
+
+static ULONG WINAPI IDirectInput8WImpl_AddRef(LPDIRECTINPUT8W iface)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
+    return IDirectInputAImpl_AddRef( (IDirectInput7A *)This );
+}
+
+static HRESULT WINAPI IDirectInput8AImpl_QueryInterface(LPDIRECTINPUT8A iface, REFIID riid, LPVOID *ppobj)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
+    return IDirectInputAImpl_QueryInterface( (IDirectInput7A *)This, riid, ppobj );
+}
+
+static HRESULT WINAPI IDirectInput8WImpl_QueryInterface(LPDIRECTINPUT8W iface, REFIID riid, LPVOID *ppobj)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
+    return IDirectInputAImpl_QueryInterface( (IDirectInput7A *)This, riid, ppobj );
+}
+
+static ULONG WINAPI IDirectInput8AImpl_Release(LPDIRECTINPUT8A iface)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
+    return IDirectInputAImpl_Release( (IDirectInput7A *)This );
+}
+
+static ULONG WINAPI IDirectInput8WImpl_Release(LPDIRECTINPUT8W iface)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
+    return IDirectInputAImpl_Release( (IDirectInput7A *)This );
+}
+
+static HRESULT WINAPI IDirectInput8AImpl_CreateDevice(LPDIRECTINPUT8A iface, REFGUID rguid,
+                                                      LPDIRECTINPUTDEVICE8A* pdev, LPUNKNOWN punk)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
+    return IDirectInput7AImpl_CreateDeviceEx( (IDirectInput7A *)This, rguid, NULL, (LPVOID*)pdev, punk );
+}
+
+static HRESULT WINAPI IDirectInput8WImpl_CreateDevice(LPDIRECTINPUT8W iface, REFGUID rguid,
+                                                      LPDIRECTINPUTDEVICE8W* pdev, LPUNKNOWN punk)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
+    return IDirectInput7WImpl_CreateDeviceEx( IDirectInput7W_from_impl( This ), rguid, NULL, (LPVOID*)pdev, punk );
+}
+
+static HRESULT WINAPI IDirectInput8AImpl_EnumDevices(LPDIRECTINPUT8A iface, DWORD dwDevType, LPDIENUMDEVICESCALLBACKA lpCallback,
+                                                     LPVOID pvRef, DWORD dwFlags)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
+    return IDirectInputAImpl_EnumDevices( (IDirectInput7A *)This, dwDevType, lpCallback, pvRef, dwFlags );
+}
+
+static HRESULT WINAPI IDirectInput8WImpl_EnumDevices(LPDIRECTINPUT8W iface, DWORD dwDevType, LPDIENUMDEVICESCALLBACKW lpCallback,
+                                                     LPVOID pvRef, DWORD dwFlags)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
+    return IDirectInputWImpl_EnumDevices( IDirectInput7W_from_impl( This ), dwDevType, lpCallback, pvRef, dwFlags );
+}
+
+static HRESULT WINAPI IDirectInput8AImpl_GetDeviceStatus(LPDIRECTINPUT8A iface, REFGUID rguid)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
+    return IDirectInputAImpl_GetDeviceStatus( (IDirectInput7A *)This, rguid );
+}
+
+static HRESULT WINAPI IDirectInput8WImpl_GetDeviceStatus(LPDIRECTINPUT8W iface, REFGUID rguid)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
+    return IDirectInputAImpl_GetDeviceStatus( (IDirectInput7A *)This, rguid );
+}
+
+static HRESULT WINAPI IDirectInput8AImpl_RunControlPanel(LPDIRECTINPUT8A iface, HWND hwndOwner, DWORD dwFlags)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
+    return IDirectInputAImpl_RunControlPanel( (IDirectInput7A *)This, hwndOwner, dwFlags );
+}
+
+static HRESULT WINAPI IDirectInput8WImpl_RunControlPanel(LPDIRECTINPUT8W iface, HWND hwndOwner, DWORD dwFlags)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
+    return IDirectInputAImpl_RunControlPanel( (IDirectInput7A *)This, hwndOwner, dwFlags );
+}
+
+static HRESULT WINAPI IDirectInput8AImpl_Initialize(LPDIRECTINPUT8A iface, HINSTANCE hinst, DWORD x)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
+    return IDirectInputAImpl_Initialize( (IDirectInput7A *)This, hinst, x );
+}
 
-      TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
-      if (IsEqualGUID(&IID_IUnknown,riid) ||
-          IsEqualGUID(&IID_IDirectInput8A,riid)) {
-              IDirectInputAImpl_AddRef((LPDIRECTINPUT7A) iface);
-              *ppobj = This;
-              return 0;
-      }
-      TRACE("Unsupported interface !\n");
-      return E_NOINTERFACE;
+static HRESULT WINAPI IDirectInput8WImpl_Initialize(LPDIRECTINPUT8W iface, HINSTANCE hinst, DWORD x)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
+    return IDirectInputAImpl_Initialize( (IDirectInput7A *)This, hinst, x );
 }
 
-static HRESULT WINAPI IDirectInput8WImpl_QueryInterface(LPDIRECTINPUT8W iface, REFIID riid, LPVOID *ppobj) {
-      IDirectInputImpl *This = (IDirectInputImpl *)iface;
+static HRESULT WINAPI IDirectInput8AImpl_FindDevice(LPDIRECTINPUT8A iface, REFGUID rguid, LPCSTR pszName, LPGUID pguidInstance)
+{
+    IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
+    return IDirectInput2AImpl_FindDevice( (IDirectInput7A *)This, rguid, pszName, pguidInstance );
+}
 
-      TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
-      if (IsEqualGUID(&IID_IUnknown,riid) ||
-          IsEqualGUID(&IID_IDirectInput8W,riid)) {
-              IDirectInputAImpl_AddRef((LPDIRECTINPUT7A) iface);
-              *ppobj = This;
-              return 0;
-      }
-      TRACE("Unsupported interface !\n");
-      return E_NOINTERFACE;
+static HRESULT WINAPI IDirectInput8WImpl_FindDevice(LPDIRECTINPUT8W iface, REFGUID rguid, LPCWSTR pszName, LPGUID pguidInstance)
+{
+    IDirectInput7W *This = IDirectInput7W_from_impl( impl_from_IDirectInput8W( iface ) );
+    return IDirectInput2WImpl_FindDevice( This, rguid, pszName, pguidInstance );
 }
 
 static HRESULT WINAPI IDirectInput8AImpl_EnumDevicesBySemantics(
@@ -485,11 +677,22 @@ static HRESULT WINAPI IDirectInput8AImpl_EnumDevicesBySemantics(
       LPVOID pvRef, DWORD dwFlags
 )
 {
-      IDirectInputImpl *This = (IDirectInputImpl *)iface;
-
-      FIXME("(this=%p,%s,%p,%p,%p,%04lx): stub\n", This, ptszUserName, lpdiActionFormat,
-            lpCallback, pvRef, dwFlags);
-      return 0;
+    IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
+
+    FIXME("(this=%p,%s,%p,%p,%p,%04x): stub\n", This, ptszUserName, lpdiActionFormat,
+          lpCallback, pvRef, dwFlags);
+#define X(x) if (dwFlags & x) FIXME("\tdwFlags |= "#x"\n");
+       X(DIEDBSFL_ATTACHEDONLY)
+       X(DIEDBSFL_THISUSER)
+       X(DIEDBSFL_FORCEFEEDBACK)
+       X(DIEDBSFL_AVAILABLEDEVICES)
+       X(DIEDBSFL_MULTIMICEKEYBOARDS)
+       X(DIEDBSFL_NONGAMINGDEVICES)
+#undef X
+
+    _dump_diactionformatA(lpdiActionFormat);
+
+    return DI_OK;
 }
 
 static HRESULT WINAPI IDirectInput8WImpl_EnumDevicesBySemantics(
@@ -498,9 +701,9 @@ static HRESULT WINAPI IDirectInput8WImpl_EnumDevicesBySemantics(
       LPVOID pvRef, DWORD dwFlags
 )
 {
-      IDirectInputImpl *This = (IDirectInputImpl *)iface;
+      IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
 
-      FIXME("(this=%p,%s,%p,%p,%p,%04lx): stub\n", This, debugstr_w(ptszUserName), lpdiActionFormat,
+      FIXME("(this=%p,%s,%p,%p,%p,%04x): stub\n", This, debugstr_w(ptszUserName), lpdiActionFormat,
             lpCallback, pvRef, dwFlags);
       return 0;
 }
@@ -510,9 +713,9 @@ static HRESULT WINAPI IDirectInput8AImpl_ConfigureDevices(
       LPDICONFIGUREDEVICESPARAMSA lpdiCDParams, DWORD dwFlags, LPVOID pvRefData
 )
 {
-      IDirectInputImpl *This = (IDirectInputImpl *)iface;
+      IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
 
-      FIXME("(this=%p,%p,%p,%04lx,%p): stub\n", This, lpdiCallback, lpdiCDParams,
+      FIXME("(this=%p,%p,%p,%04x,%p): stub\n", This, lpdiCallback, lpdiCDParams,
             dwFlags, pvRefData);
       return 0;
 }
@@ -522,93 +725,66 @@ static HRESULT WINAPI IDirectInput8WImpl_ConfigureDevices(
       LPDICONFIGUREDEVICESPARAMSW lpdiCDParams, DWORD dwFlags, LPVOID pvRefData
 )
 {
-      IDirectInputImpl *This = (IDirectInputImpl *)iface;
+      IDirectInputImpl *This = impl_from_IDirectInput8W( iface );
 
-      FIXME("(this=%p,%p,%p,%04lx,%p): stub\n", This, lpdiCallback, lpdiCDParams,
+      FIXME("(this=%p,%p,%p,%04x,%p): stub\n", This, lpdiCallback, lpdiCDParams,
             dwFlags, pvRefData);
       return 0;
 }
 
-#if !defined(__STRICT_ANSI__) && defined(__GNUC__)
-# define XCAST(fun)   (typeof(ddi7avt.fun))
-#else
-# define XCAST(fun)    (void*)
-#endif
-
 static const IDirectInput7AVtbl ddi7avt = {
-       XCAST(QueryInterface)IDirectInputAImpl_QueryInterface,
-       XCAST(AddRef)IDirectInputAImpl_AddRef,
-       XCAST(Release)IDirectInputAImpl_Release,
-       XCAST(CreateDevice)IDirectInputAImpl_CreateDevice,
-       XCAST(EnumDevices)IDirectInputAImpl_EnumDevices,
-       XCAST(GetDeviceStatus)IDirectInputAImpl_GetDeviceStatus,
-       XCAST(RunControlPanel)IDirectInputAImpl_RunControlPanel,
-       XCAST(Initialize)IDirectInputAImpl_Initialize,
-       XCAST(FindDevice)IDirectInput2AImpl_FindDevice,
-       XCAST(CreateDeviceEx)IDirectInput7AImpl_CreateDeviceEx
+    IDirectInputAImpl_QueryInterface,
+    IDirectInputAImpl_AddRef,
+    IDirectInputAImpl_Release,
+    IDirectInputAImpl_CreateDevice,
+    IDirectInputAImpl_EnumDevices,
+    IDirectInputAImpl_GetDeviceStatus,
+    IDirectInputAImpl_RunControlPanel,
+    IDirectInputAImpl_Initialize,
+    IDirectInput2AImpl_FindDevice,
+    IDirectInput7AImpl_CreateDeviceEx
 };
 
-#undef XCAST
-#if !defined(__STRICT_ANSI__) && defined(__GNUC__)
-# define XCAST(fun)   (typeof(ddi7wvt.fun))
-#else
-# define XCAST(fun)    (void*)
-#endif
-
 static const IDirectInput7WVtbl ddi7wvt = {
-       XCAST(QueryInterface)IDirectInputWImpl_QueryInterface,
-       XCAST(AddRef)IDirectInputAImpl_AddRef,
-       XCAST(Release)IDirectInputAImpl_Release,
-       XCAST(CreateDevice)IDirectInputWImpl_CreateDevice,
-       XCAST(EnumDevices)IDirectInputWImpl_EnumDevices,
-       XCAST(GetDeviceStatus)IDirectInputAImpl_GetDeviceStatus,
-       XCAST(RunControlPanel)IDirectInputAImpl_RunControlPanel,
-       XCAST(Initialize)IDirectInputAImpl_Initialize,
-       XCAST(FindDevice)IDirectInput2WImpl_FindDevice,
-       XCAST(CreateDeviceEx)IDirectInput7WImpl_CreateDeviceEx
+    IDirectInputWImpl_QueryInterface,
+    IDirectInputWImpl_AddRef,
+    IDirectInputWImpl_Release,
+    IDirectInputWImpl_CreateDevice,
+    IDirectInputWImpl_EnumDevices,
+    IDirectInputWImpl_GetDeviceStatus,
+    IDirectInputWImpl_RunControlPanel,
+    IDirectInputWImpl_Initialize,
+    IDirectInput2WImpl_FindDevice,
+    IDirectInput7WImpl_CreateDeviceEx
 };
-#undef XCAST
-
-#if !defined(__STRICT_ANSI__) && defined(__GNUC__)
-# define XCAST(fun)    (typeof(ddi8avt.fun))
-#else
-# define XCAST(fun)    (void*)
-#endif
 
 static const IDirectInput8AVtbl ddi8avt = {
-       XCAST(QueryInterface)IDirectInput8AImpl_QueryInterface,
-       XCAST(AddRef)IDirectInputAImpl_AddRef,
-       XCAST(Release)IDirectInputAImpl_Release,
-       XCAST(CreateDevice)IDirectInputAImpl_CreateDevice,
-       XCAST(EnumDevices)IDirectInputAImpl_EnumDevices,
-       XCAST(GetDeviceStatus)IDirectInputAImpl_GetDeviceStatus,
-       XCAST(RunControlPanel)IDirectInputAImpl_RunControlPanel,
-       XCAST(Initialize)IDirectInputAImpl_Initialize,
-       XCAST(FindDevice)IDirectInput2AImpl_FindDevice,
-       XCAST(EnumDevicesBySemantics)IDirectInput8AImpl_EnumDevicesBySemantics,
-       XCAST(ConfigureDevices)IDirectInput8AImpl_ConfigureDevices
+    IDirectInput8AImpl_QueryInterface,
+    IDirectInput8AImpl_AddRef,
+    IDirectInput8AImpl_Release,
+    IDirectInput8AImpl_CreateDevice,
+    IDirectInput8AImpl_EnumDevices,
+    IDirectInput8AImpl_GetDeviceStatus,
+    IDirectInput8AImpl_RunControlPanel,
+    IDirectInput8AImpl_Initialize,
+    IDirectInput8AImpl_FindDevice,
+    IDirectInput8AImpl_EnumDevicesBySemantics,
+    IDirectInput8AImpl_ConfigureDevices
 };
-#undef XCAST
 
-#if !defined(__STRICT_ANSI__) && defined(__GNUC__)
-# define XCAST(fun)    (typeof(ddi8wvt.fun))
-#else
-# define XCAST(fun)    (void*)
-#endif
 static const IDirectInput8WVtbl ddi8wvt = {
-       XCAST(QueryInterface)IDirectInput8WImpl_QueryInterface,
-       XCAST(AddRef)IDirectInputAImpl_AddRef,
-       XCAST(Release)IDirectInputAImpl_Release,
-       XCAST(CreateDevice)IDirectInputWImpl_CreateDevice,
-       XCAST(EnumDevices)IDirectInputWImpl_EnumDevices,
-       XCAST(GetDeviceStatus)IDirectInputAImpl_GetDeviceStatus,
-       XCAST(RunControlPanel)IDirectInputAImpl_RunControlPanel,
-       XCAST(Initialize)IDirectInputAImpl_Initialize,
-       XCAST(FindDevice)IDirectInput2WImpl_FindDevice,
-       XCAST(EnumDevicesBySemantics)IDirectInput8WImpl_EnumDevicesBySemantics,
-       XCAST(ConfigureDevices)IDirectInput8WImpl_ConfigureDevices
+    IDirectInput8WImpl_QueryInterface,
+    IDirectInput8WImpl_AddRef,
+    IDirectInput8WImpl_Release,
+    IDirectInput8WImpl_CreateDevice,
+    IDirectInput8WImpl_EnumDevices,
+    IDirectInput8WImpl_GetDeviceStatus,
+    IDirectInput8WImpl_RunControlPanel,
+    IDirectInput8WImpl_Initialize,
+    IDirectInput8WImpl_FindDevice,
+    IDirectInput8WImpl_EnumDevicesBySemantics,
+    IDirectInput8WImpl_ConfigureDevices
 };
-#undef XCAST
 
 /*******************************************************************************
  * DirectInput ClassFactory
@@ -644,7 +820,8 @@ static HRESULT WINAPI DICF_CreateInstance(
        IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
 
        TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
-       if ( IsEqualGUID( &IID_IDirectInputA, riid ) ||
+        if ( IsEqualGUID( &IID_IUnknown, riid ) ||
+             IsEqualGUID( &IID_IDirectInputA, riid ) ||
             IsEqualGUID( &IID_IDirectInputW, riid ) ||
             IsEqualGUID( &IID_IDirectInput2A, riid ) ||
             IsEqualGUID( &IID_IDirectInput2W, riid ) ||
@@ -656,7 +833,7 @@ static HRESULT WINAPI DICF_CreateInstance(
                return DirectInputCreateEx(0,0,riid,ppobj,pOuter);
        }
 
-       FIXME("(%p,%p,%s,%p) Interface not found!\n",This,pOuter,debugstr_guid(riid),ppobj);
+       FIXME("(%p,%p,%s,%p) Interface not found!\n",This,pOuter,debugstr_guid(riid),ppobj);    
        return E_NOINTERFACE;
 }
 
@@ -692,7 +869,7 @@ HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
 {
     TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
     if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
-       *ppv = (LPVOID)&DINPUT_CF;
+        *ppv = &DINPUT_CF;
        IClassFactory_AddRef((IClassFactory*)*ppv);
     return S_OK;
     }
@@ -700,3 +877,224 @@ HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
     FIXME("(%s,%s,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
     return CLASS_E_CLASSNOTAVAILABLE;
 }
+
+/******************************************************************************
+ *     DInput hook thread
+ */
+
+static LRESULT CALLBACK LL_hook_proc( int code, WPARAM wparam, LPARAM lparam )
+{
+    IDirectInputImpl *dinput;
+
+    if (code != HC_ACTION) return CallNextHookEx( 0, code, wparam, lparam );
+
+    EnterCriticalSection( &dinput_hook_crit );
+    LIST_FOR_EACH_ENTRY( dinput, &direct_input_list, IDirectInputImpl, entry )
+    {
+        IDirectInputDevice2AImpl *dev;
+
+        EnterCriticalSection( &dinput->crit );
+        LIST_FOR_EACH_ENTRY( dev, &dinput->devices_list, IDirectInputDevice2AImpl, entry )
+            if (dev->acquired && dev->event_proc)
+            {
+                TRACE("calling %p->%p (%lx %lx)\n", dev, dev->event_proc, wparam, lparam);
+                dev->event_proc( (LPDIRECTINPUTDEVICE8A)dev, wparam, lparam );
+            }
+        LeaveCriticalSection( &dinput->crit );
+    }
+    LeaveCriticalSection( &dinput_hook_crit );
+
+    return CallNextHookEx( 0, code, wparam, lparam );
+}
+
+static LRESULT CALLBACK callwndproc_proc( int code, WPARAM wparam, LPARAM lparam )
+{
+    CWPSTRUCT *msg = (CWPSTRUCT *)lparam;
+    IDirectInputImpl *dinput;
+    HWND foreground;
+
+    if (code != HC_ACTION || (msg->message != WM_KILLFOCUS &&
+        msg->message != WM_ACTIVATEAPP && msg->message != WM_ACTIVATE))
+        return CallNextHookEx( 0, code, wparam, lparam );
+
+    foreground = GetForegroundWindow();
+
+    EnterCriticalSection( &dinput_hook_crit );
+
+    LIST_FOR_EACH_ENTRY( dinput, &direct_input_list, IDirectInputImpl, entry )
+    {
+        IDirectInputDevice2AImpl *dev;
+
+        EnterCriticalSection( &dinput->crit );
+        LIST_FOR_EACH_ENTRY( dev, &dinput->devices_list, IDirectInputDevice2AImpl, entry )
+        {
+            if (!dev->acquired) continue;
+
+            if (msg->hwnd == dev->win && msg->hwnd != foreground)
+            {
+                TRACE( "%p window is not foreground - unacquiring %p\n", dev->win, dev );
+                IDirectInputDevice_Unacquire( (LPDIRECTINPUTDEVICE8A)dev );
+            }
+        }
+        LeaveCriticalSection( &dinput->crit );
+    }
+    LeaveCriticalSection( &dinput_hook_crit );
+
+    return CallNextHookEx( 0, code, wparam, lparam );
+}
+
+static DWORD WINAPI hook_thread_proc(void *param)
+{
+    static HHOOK kbd_hook, mouse_hook;
+    MSG msg;
+
+    /* Force creation of the message queue */
+    PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE );
+    SetEvent(*(LPHANDLE)param);
+
+    while (GetMessageW( &msg, 0, 0, 0 ))
+    {
+        UINT kbd_cnt = 0, mice_cnt = 0;
+
+        if (msg.message == WM_USER+0x10)
+        {
+            IDirectInputImpl *dinput;
+
+            TRACE( "Processing hook change notification lp:%ld\n", msg.lParam );
+
+            if (!msg.wParam && !msg.lParam)
+            {
+                if (kbd_hook) UnhookWindowsHookEx( kbd_hook );
+                if (mouse_hook) UnhookWindowsHookEx( mouse_hook );
+                kbd_hook = mouse_hook = NULL;
+                break;
+            }
+
+            EnterCriticalSection( &dinput_hook_crit );
+
+            /* Count acquired keyboards and mice*/
+            LIST_FOR_EACH_ENTRY( dinput, &direct_input_list, IDirectInputImpl, entry )
+            {
+                IDirectInputDevice2AImpl *dev;
+
+                EnterCriticalSection( &dinput->crit );
+                LIST_FOR_EACH_ENTRY( dev, &dinput->devices_list, IDirectInputDevice2AImpl, entry )
+                {
+                    if (!dev->acquired || !dev->event_proc) continue;
+
+                    if (IsEqualGUID( &dev->guid, &GUID_SysKeyboard ) ||
+                        IsEqualGUID( &dev->guid, &DInput_Wine_Keyboard_GUID ))
+                        kbd_cnt++;
+                    else
+                        if (IsEqualGUID( &dev->guid, &GUID_SysMouse ) ||
+                            IsEqualGUID( &dev->guid, &DInput_Wine_Mouse_GUID ))
+                            mice_cnt++;
+                }
+                LeaveCriticalSection( &dinput->crit );
+            }
+            LeaveCriticalSection( &dinput_hook_crit );
+
+            if (kbd_cnt && !kbd_hook)
+                kbd_hook = SetWindowsHookExW( WH_KEYBOARD_LL, LL_hook_proc, DINPUT_instance, 0 );
+            else if (!kbd_cnt && kbd_hook)
+            {
+                UnhookWindowsHookEx( kbd_hook );
+                kbd_hook = NULL;
+            }
+
+            if (mice_cnt && !mouse_hook)
+                mouse_hook = SetWindowsHookExW( WH_MOUSE_LL, LL_hook_proc, DINPUT_instance, 0 );
+            else if (!mice_cnt && mouse_hook)
+            {
+                UnhookWindowsHookEx( mouse_hook );
+                mouse_hook = NULL;
+            }
+        }
+        TranslateMessage(&msg);
+        DispatchMessageW(&msg);
+    }
+
+    return 0;
+}
+
+static DWORD hook_thread_id;
+
+static CRITICAL_SECTION_DEBUG dinput_critsect_debug =
+{
+    0, 0, &dinput_hook_crit,
+    { &dinput_critsect_debug.ProcessLocksList, &dinput_critsect_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": dinput_hook_crit") }
+};
+static CRITICAL_SECTION dinput_hook_crit = { &dinput_critsect_debug, -1, 0, 0, 0, 0 };
+
+static BOOL check_hook_thread(void)
+{
+    static HANDLE hook_thread;
+
+    EnterCriticalSection(&dinput_hook_crit);
+
+    TRACE("IDirectInputs left: %d\n", list_count(&direct_input_list));
+    if (!list_empty(&direct_input_list) && !hook_thread)
+    {
+        HANDLE event;
+
+        event = CreateEventW(NULL, FALSE, FALSE, NULL);
+        hook_thread = CreateThread(NULL, 0, hook_thread_proc, &event, 0, &hook_thread_id);
+        if (event && hook_thread)
+        {
+            HANDLE handles[2];
+            handles[0] = event;
+            handles[1] = hook_thread;
+            WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+        }
+        LeaveCriticalSection(&dinput_hook_crit);
+        CloseHandle(event);
+    }
+    else if (list_empty(&direct_input_list) && hook_thread)
+    {
+        DWORD tid = hook_thread_id;
+
+        hook_thread_id = 0;
+        PostThreadMessageW(tid, WM_USER+0x10, 0, 0);
+        LeaveCriticalSection(&dinput_hook_crit);
+
+        /* wait for hook thread to exit */
+        WaitForSingleObject(hook_thread, INFINITE);
+        CloseHandle(hook_thread);
+        hook_thread = NULL;
+    }
+    else
+        LeaveCriticalSection(&dinput_hook_crit);
+
+    return hook_thread_id != 0;
+}
+
+void check_dinput_hooks(LPDIRECTINPUTDEVICE8A iface)
+{
+    static HHOOK callwndproc_hook;
+    static ULONG foreground_cnt;
+    IDirectInputDevice2AImpl *dev = (IDirectInputDevice2AImpl *)iface;
+
+    EnterCriticalSection(&dinput_hook_crit);
+
+    if (dev->dwCoopLevel & DISCL_FOREGROUND)
+    {
+        if (dev->acquired)
+            foreground_cnt++;
+        else
+            foreground_cnt--;
+    }
+
+    if (foreground_cnt && !callwndproc_hook)
+        callwndproc_hook = SetWindowsHookExW( WH_CALLWNDPROC, callwndproc_proc,
+                                              DINPUT_instance, GetCurrentThreadId() );
+    else if (!foreground_cnt && callwndproc_hook)
+    {
+        UnhookWindowsHookEx( callwndproc_hook );
+        callwndproc_hook = NULL;
+    }
+
+    PostThreadMessageW( hook_thread_id, WM_USER+0x10, 1, 0 );
+
+    LeaveCriticalSection(&dinput_hook_crit);
+}
index 5138581..15783e2 100644 (file)
@@ -13,7 +13,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #ifndef __WINE_DLLS_DINPUT_DINPUT_PRIVATE_H
 #include "windef.h"
 #include "winbase.h"
 #include "dinput.h"
+#include "wine/list.h"
 
 /* Implementation specification */
 typedef struct IDirectInputImpl IDirectInputImpl;
 struct IDirectInputImpl
 {
-   const void *lpVtbl;
-   LONG  ref;
+    const IDirectInput7AVtbl   *lpVtbl;
+    const IDirectInput7WVtbl   *lpVtbl7w;
+    const IDirectInput8AVtbl   *lpVtbl8a;
+    const IDirectInput8WVtbl   *lpVtbl8w;
 
-   /* Used to have an unique sequence number for all the events */
-   DWORD evsequence;
+    LONG                        ref;
 
-   DWORD dwVersion;
+    CRITICAL_SECTION            crit;
+    struct list                 entry;          /* entry into list of all IDirectInputs */
+
+    DWORD                       evsequence;     /* unique sequence number for events */
+    DWORD                       dwVersion;      /* direct input version number */
+    struct list                 devices_list;   /* list of all created dinput devices */
 };
 
 /* Function called by all devices that Wine supports */
@@ -52,6 +59,9 @@ extern const struct dinput_device keyboard_device;
 extern const struct dinput_device joystick_linux_device;
 extern const struct dinput_device joystick_linuxinput_device;
 
-extern HINSTANCE DINPUT_instance;
+extern void check_dinput_hooks(LPDIRECTINPUTDEVICE8A);
+typedef void (*DI_EVENT_PROC)(LPDIRECTINPUTDEVICE8A, WPARAM, LPARAM);
+
+extern void _dump_diactionformatA(LPDIACTIONFORMATA);
 
 #endif /* __WINE_DLLS_DINPUT_DINPUT_PRIVATE_H */
index 7753196..155d800 100644 (file)
@@ -16,7 +16,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #include "config.h"
@@ -27,6 +27,7 @@
 #include <string.h>
 #ifdef HAVE_LINUX_INPUT_H
 #  include <linux/input.h>
+#  undef SW_MAX
 #endif
 #include <errno.h>
 #ifdef HAVE_UNISTD_H
@@ -52,11 +53,10 @@ struct LinuxInputEffectImpl
     LONG       ref;
     GUID       guid;
 
-    /* Effect data */
-    struct ff_effect effect;
-
-    /* Parent device */
-    int*       fd;
+    struct ff_effect    effect; /* Effect data */
+    int                 gain;   /* Effect gain */
+    int*                fd;     /* Parent device */
+    struct list        *entry;  /* Entry into the parent's list of effects */
 };
 
 
@@ -91,7 +91,7 @@ static DWORD _typeFromGUID(REFGUID guid)
 
 
 /******************************************************************************
- *      DirectInputEffect debug helpers
+ *      DirectInputEffect debug helpers 
  */
 
 static void _dump_DIEFFECT_flags(DWORD dwFlags)
@@ -112,59 +112,58 @@ static void _dump_DIEFFECT_flags(DWORD dwFlags)
         };
         for (i = 0; i < (sizeof(flags) / sizeof(flags[0])); i++)
             if (flags[i].mask & dwFlags)
-                DPRINTF("%s ", flags[i].name);
-        DPRINTF("\n");
-    }
+                TRACE("%s ", flags[i].name);
+        TRACE("\n");
+    }       
 }
 
-static void _dump_DIENVELOPE(LPDIENVELOPE env)
+static void _dump_DIENVELOPE(LPCDIENVELOPE env)
 {
     if (env->dwSize != sizeof(DIENVELOPE)) {
-        WARN("Non-standard DIENVELOPE structure size (%ld instead of %d).\n",
-            env->dwSize, sizeof(DIENVELOPE));
+        WARN("Non-standard DIENVELOPE structure size %d.\n", env->dwSize);
     }
-    TRACE("Envelope has attack (level: %ld time: %ld), fade (level: %ld time: %ld)\n",
+    TRACE("Envelope has attack (level: %d time: %d), fade (level: %d time: %d)\n",
          env->dwAttackLevel, env->dwAttackTime, env->dwFadeLevel, env->dwFadeTime);
-}
+} 
 
-static void _dump_DICONSTANTFORCE(LPDICONSTANTFORCE frc)
+static void _dump_DICONSTANTFORCE(LPCDICONSTANTFORCE frc)
 {
-    TRACE("Constant force has magnitude %ld\n", frc->lMagnitude);
+    TRACE("Constant force has magnitude %d\n", frc->lMagnitude);
 }
 
-static void _dump_DIPERIODIC(LPDIPERIODIC frc)
+static void _dump_DIPERIODIC(LPCDIPERIODIC frc)
 {
-    TRACE("Periodic force has magnitude %ld, offset %ld, phase %ld, period %ld\n",
+    TRACE("Periodic force has magnitude %d, offset %d, phase %d, period %d\n",
          frc->dwMagnitude, frc->lOffset, frc->dwPhase, frc->dwPeriod);
 }
 
-static void _dump_DIRAMPFORCE(LPDIRAMPFORCE frc)
+static void _dump_DIRAMPFORCE(LPCDIRAMPFORCE frc)
 {
-    TRACE("Ramp force has start %ld, end %ld\n",
+    TRACE("Ramp force has start %d, end %d\n",
          frc->lStart, frc->lEnd);
 }
 
-static void _dump_DICONDITION(LPDICONDITION frc)
+static void _dump_DICONDITION(LPCDICONDITION frc)
 {
-    TRACE("Condition has offset %ld, pos/neg coefficients %ld and %ld, pos/neg saturations %ld and %ld, deadband %ld\n",
+    TRACE("Condition has offset %d, pos/neg coefficients %d and %d, pos/neg saturations %d and %d, deadband %d\n",
          frc->lOffset, frc->lPositiveCoefficient, frc->lNegativeCoefficient,
          frc->dwPositiveSaturation, frc->dwNegativeSaturation, frc->lDeadBand);
 }
 
-static void _dump_DICUSTOMFORCE(LPDICUSTOMFORCE frc)
+static void _dump_DICUSTOMFORCE(LPCDICUSTOMFORCE frc)
 {
     unsigned int i;
-    TRACE("Custom force uses %ld channels, sample period %ld.  Has %ld samples at %p.\n",
+    TRACE("Custom force uses %d channels, sample period %d.  Has %d samples at %p.\n",
          frc->cChannels, frc->dwSamplePeriod, frc->cSamples, frc->rglForceData);
     if (frc->cSamples % frc->cChannels != 0)
        WARN("Custom force has a non-integral samples-per-channel count!\n");
     if (TRACE_ON(dinput)) {
-       DPRINTF("Custom force data (time aligned, axes in order):\n");
+       TRACE("Custom force data (time aligned, axes in order):\n");
        for (i = 1; i <= frc->cSamples; ++i) {
-           DPRINTF("%ld ", frc->rglForceData[i]);
+           TRACE("%d ", frc->rglForceData[i]);
            if (i % frc->cChannels == 0)
-               DPRINTF("\n");
-       }
+               TRACE("\n");
+       }       
     }
 }
 
@@ -174,43 +173,42 @@ static void _dump_DIEFFECT(LPCDIEFFECT eff, REFGUID guid)
     DWORD type = _typeFromGUID(guid);
 
     TRACE("Dumping DIEFFECT structure:\n");
-    TRACE("  - dwSize: %ld\n", eff->dwSize);
+    TRACE("  - dwSize: %d\n", eff->dwSize);
     if ((eff->dwSize != sizeof(DIEFFECT)) && (eff->dwSize != sizeof(DIEFFECT_DX5))) {
-        WARN("Non-standard DIEFFECT structure size (%ld instead of %d or %d).\n",
-            eff->dwSize, sizeof(DIEFFECT), sizeof(DIEFFECT_DX5));
+        WARN("Non-standard DIEFFECT structure size %d\n", eff->dwSize);
     }
-    TRACE("  - dwFlags: %ld\n", eff->dwFlags);
+    TRACE("  - dwFlags: %d\n", eff->dwFlags);
     TRACE("    ");
-    _dump_DIEFFECT_flags(eff->dwFlags);
-    TRACE("  - dwDuration: %ld\n", eff->dwDuration);
-    TRACE("  - dwGain: %ld\n", eff->dwGain);
-    if ((eff->dwGain > 10000) || (eff->dwGain < 0))
-       WARN("dwGain is out of range (0 - 10,000)\n");
-    TRACE("  - dwTriggerButton: %ld\n", eff->dwTriggerButton);
-    TRACE("  - dwTriggerRepeatInterval: %ld\n", eff->dwTriggerRepeatInterval);
-    TRACE("  - cAxes: %ld\n", eff->cAxes);
+    _dump_DIEFFECT_flags(eff->dwFlags); 
+    TRACE("  - dwDuration: %d\n", eff->dwDuration);
+    TRACE("  - dwGain: %d\n", eff->dwGain);
+    if (eff->dwGain > 10000)
+       WARN("dwGain is out of range (>10,000)\n");
+    TRACE("  - dwTriggerButton: %d\n", eff->dwTriggerButton);
+    TRACE("  - dwTriggerRepeatInterval: %d\n", eff->dwTriggerRepeatInterval);
+    TRACE("  - cAxes: %d\n", eff->cAxes);
     TRACE("  - rgdwAxes: %p\n", eff->rgdwAxes);
-    if (TRACE_ON(dinput)) {
-       TRACE("    ");
+    if (TRACE_ON(dinput) && eff->rgdwAxes) {
+       TRACE("    ");  
        for (i = 0; i < eff->cAxes; ++i)
-           DPRINTF("%ld ", eff->rgdwAxes[i]);
-       DPRINTF("\n");
+           TRACE("%d ", eff->rgdwAxes[i]);
+       TRACE("\n");
     }
     TRACE("  - rglDirection: %p\n", eff->rglDirection);
     TRACE("  - lpEnvelope: %p\n", eff->lpEnvelope);
-    TRACE("  - cbTypeSpecificParams: %ld\n", eff->cbTypeSpecificParams);
+    TRACE("  - cbTypeSpecificParams: %d\n", eff->cbTypeSpecificParams);
     TRACE("  - lpvTypeSpecificParams: %p\n", eff->lpvTypeSpecificParams);
     if (eff->dwSize > sizeof(DIEFFECT_DX5))
-       TRACE("  - dwStartDelay: %ld\n", eff->dwStartDelay);
+       TRACE("  - dwStartDelay: %d\n", eff->dwStartDelay);
     if (eff->lpEnvelope != NULL)
        _dump_DIENVELOPE(eff->lpEnvelope);
     if (type == DIEFT_CONSTANTFORCE) {
        if (eff->cbTypeSpecificParams != sizeof(DICONSTANTFORCE)) {
-           WARN("Effect claims to be a constant force but the type-specific params are the wrong size!\n");
+           WARN("Effect claims to be a constant force but the type-specific params are the wrong size!\n"); 
        } else {
            _dump_DICONSTANTFORCE(eff->lpvTypeSpecificParams);
        }
-    } else if (type == DIEFT_PERIODIC) {
+    } else if (type == DIEFT_PERIODIC) { 
         if (eff->cbTypeSpecificParams != sizeof(DIPERIODIC)) {
             WARN("Effect claims to be a periodic force but the type-specific params are the wrong size!\n");
         } else {
@@ -222,7 +220,7 @@ static void _dump_DIEFFECT(LPCDIEFFECT eff, REFGUID guid)
         } else {
             _dump_DIRAMPFORCE(eff->lpvTypeSpecificParams);
         }
-    } else if (type == DIEFT_CONDITION) {
+    } else if (type == DIEFT_CONDITION) { 
         if (eff->cbTypeSpecificParams != sizeof(DICONDITION)) {
             WARN("Effect claims to be a condition but the type-specific params are the wrong size!\n");
         } else {
@@ -239,7 +237,7 @@ static void _dump_DIEFFECT(LPCDIEFFECT eff, REFGUID guid)
 
 
 /******************************************************************************
- *      LinuxInputEffectImpl
+ *      LinuxInputEffectImpl 
  */
 
 static ULONG WINAPI LinuxInputEffectImpl_AddRef(
@@ -260,7 +258,7 @@ static HRESULT WINAPI LinuxInputEffectImpl_Download(
        if (errno == ENOMEM) {
            return DIERR_DEVICEFULL;
        } else {
-           FIXME("Could not upload effect. Assuming a disconnected device.\n");
+            FIXME("Could not upload effect. Assuming a disconnected device %d \"%s\".\n", *This->fd, strerror(errno));
            return DIERR_INPUTLOST;
        }
     }
@@ -272,7 +270,7 @@ static HRESULT WINAPI LinuxInputEffectImpl_Escape(
        LPDIRECTINPUTEFFECT iface,
         LPDIEFFESCAPE pesc)
 {
-    WARN("(this=%p,%p): invalid: no hardware-specific escape codes in this"
+    WARN("(this=%p,%p): invalid: no hardware-specific escape codes in this" 
          " driver!\n", iface, pesc);
 
     return DI_OK;
@@ -287,7 +285,7 @@ static HRESULT WINAPI LinuxInputEffectImpl_GetEffectGuid(
     TRACE("(this=%p,%p)\n", This, pguid);
 
     pguid = &This->guid;
-
+    
     return DI_OK;
 }
 
@@ -314,7 +312,7 @@ static HRESULT WINAPI LinuxInputEffectImpl_GetParameters(
 {
     HRESULT diErr = DI_OK;
     LinuxInputEffectImpl *This = (LinuxInputEffectImpl *)iface;
-    TRACE("(this=%p,%p,%ld)\n", This, peff, dwFlags);
+    TRACE("(this=%p,%p,%d)\n", This, peff, dwFlags);
 
     /* Major conversion factors are:
      * times: millisecond (linux) -> microsecond (windows) (x * 1000)
@@ -326,7 +324,7 @@ static HRESULT WINAPI LinuxInputEffectImpl_GetParameters(
     if (dwFlags & DIEP_AXES) {
        if (peff->cAxes < 2 /* linuxinput effects always use 2 axes, x and y */)
            diErr = DIERR_MOREDATA;
-       peff->cAxes = 2;
+       peff->cAxes = 2; 
        if (diErr)
            return diErr;
        else {
@@ -334,11 +332,11 @@ static HRESULT WINAPI LinuxInputEffectImpl_GetParameters(
            peff->rgdwAxes[1] = DIJOFS_Y;
        }
     }
-
     if (dwFlags & DIEP_DIRECTION) {
         if (peff->cAxes < 2)
             diErr = DIERR_MOREDATA;
-        peff->cAxes = 2;
+        peff->cAxes = 2; 
         if (diErr)
             return diErr;
         else {
@@ -373,7 +371,7 @@ static HRESULT WINAPI LinuxInputEffectImpl_GetParameters(
            peff->lpEnvelope = NULL;
        } else if (peff->lpEnvelope == NULL) {
            return DIERR_INVALIDPARAM;
-       } else {
+       } else { 
            peff->lpEnvelope->dwAttackLevel = (env->attack_level / 33) * 10;
            peff->lpEnvelope->dwAttackTime = env->attack_length * 1000;
            peff->lpEnvelope->dwFadeLevel = (env->fade_level / 33) * 10;
@@ -382,9 +380,7 @@ static HRESULT WINAPI LinuxInputEffectImpl_GetParameters(
     }
 
     if (dwFlags & DIEP_GAIN) {
-       /* the linux input ff driver apparently has no support
-         * for setting the device's gain. */
-       peff->dwGain = DI_FFNOMINALMAX;
+       peff->dwGain = This->gain * 10000 / 0xFFFF;
     }
 
     if (dwFlags & DIEP_SAMPLEPERIOD) {
@@ -407,14 +403,14 @@ static HRESULT WINAPI LinuxInputEffectImpl_GetParameters(
     }
 
     if (dwFlags & DIEP_TYPESPECIFICPARAMS) {
-       int expectedsize = 0;
+       DWORD expectedsize = 0;
        if (This->effect.type == FF_PERIODIC) {
            expectedsize = sizeof(DIPERIODIC);
        } else if (This->effect.type == FF_CONSTANT) {
            expectedsize = sizeof(DICONSTANTFORCE);
-       } else if (This->effect.type == FF_SPRING
-               || This->effect.type == FF_FRICTION
-               || This->effect.type == FF_INERTIA
+       } else if (This->effect.type == FF_SPRING 
+               || This->effect.type == FF_FRICTION 
+               || This->effect.type == FF_INERTIA 
                || This->effect.type == FF_DAMPER) {
            expectedsize = sizeof(DICONDITION) * 2;
        } else if (This->effect.type == FF_RAMP) {
@@ -427,35 +423,35 @@ static HRESULT WINAPI LinuxInputEffectImpl_GetParameters(
            return diErr;
        else {
            if (This->effect.type == FF_PERIODIC) {
-                LPDIPERIODIC tsp = (LPDIPERIODIC)(peff->lpvTypeSpecificParams);
+                LPDIPERIODIC tsp = peff->lpvTypeSpecificParams;
                tsp->dwMagnitude = (This->effect.u.periodic.magnitude / 33) * 10;
                tsp->lOffset = (This->effect.u.periodic.offset / 33) * 10;
                tsp->dwPhase = (This->effect.u.periodic.phase / 33) * 36;
                tsp->dwPeriod = (This->effect.u.periodic.period * 1000);
            } else if (This->effect.type == FF_CONSTANT) {
-                LPDICONSTANTFORCE tsp = (LPDICONSTANTFORCE)(peff->lpvTypeSpecificParams);
+                LPDICONSTANTFORCE tsp = peff->lpvTypeSpecificParams;
                tsp->lMagnitude = (This->effect.u.constant.level / 33) * 10;
-           } else if (This->effect.type == FF_SPRING
-                   || This->effect.type == FF_FRICTION
-                   || This->effect.type == FF_INERTIA
+           } else if (This->effect.type == FF_SPRING 
+                   || This->effect.type == FF_FRICTION 
+                   || This->effect.type == FF_INERTIA 
                    || This->effect.type == FF_DAMPER) {
-                LPDICONDITION tsp = (LPDICONDITION)(peff->lpvTypeSpecificParams);
+                LPDICONDITION tsp = peff->lpvTypeSpecificParams;
                int i;
                for (i = 0; i < 2; ++i) {
-                   tsp[i].lOffset = (This->effect.u.condition[i].center / 33) * 10;
+                   tsp[i].lOffset = (This->effect.u.condition[i].center / 33) * 10; 
                    tsp[i].lPositiveCoefficient = (This->effect.u.condition[i].right_coeff / 33) * 10;
-                   tsp[i].lNegativeCoefficient = (This->effect.u.condition[i].left_coeff / 33) * 10;
+                   tsp[i].lNegativeCoefficient = (This->effect.u.condition[i].left_coeff / 33) * 10; 
                    tsp[i].dwPositiveSaturation = (This->effect.u.condition[i].right_saturation / 33) * 10;
                    tsp[i].dwNegativeSaturation = (This->effect.u.condition[i].left_saturation / 33) * 10;
                    tsp[i].lDeadBand = (This->effect.u.condition[i].deadband / 33) * 10;
                }
            } else if (This->effect.type == FF_RAMP) {
-                LPDIRAMPFORCE tsp = (LPDIRAMPFORCE)(peff->lpvTypeSpecificParams);
+                LPDIRAMPFORCE tsp = peff->lpvTypeSpecificParams;
                tsp->lStart = (This->effect.u.ramp.start_level / 33) * 10;
                tsp->lEnd = (This->effect.u.ramp.end_level / 33) * 10;
            }
        }
-    }
+    } 
 
     return diErr;
 }
@@ -466,7 +462,7 @@ static HRESULT WINAPI LinuxInputEffectImpl_Initialize(
        DWORD dwVersion,
        REFGUID rguid)
 {
-    FIXME("(this=%p,%p,%ld,%s): stub!\n",
+    FIXME("(this=%p,%p,%d,%s): stub!\n",
         iface, hinst, dwVersion, debugstr_guid(rguid));
 
     return DI_OK;
@@ -500,7 +496,7 @@ static HRESULT WINAPI LinuxInputEffectImpl_Start(
     struct input_event event;
     LinuxInputEffectImpl* This = (LinuxInputEffectImpl*)iface;
 
-    TRACE("(this=%p,%ld,%ld)\n", This, dwIterations, dwFlags);
+    TRACE("(this=%p,%d,%d)\n", This, dwIterations, dwFlags);
 
     if (!(dwFlags & DIES_NODOWNLOAD)) {
        /* Download the effect if necessary */
@@ -516,6 +512,12 @@ static HRESULT WINAPI LinuxInputEffectImpl_Start(
     }
 
     event.type = EV_FF;
+
+    event.code = FF_GAIN;
+    event.value = This->gain;
+    if (write(*(This->fd), &event, sizeof(event)) == -1)
+       FIXME("Failed setting gain. Error: %d \"%s\".\n", errno, strerror(errno));
+
     event.code = This->effect.id;
     event.value = dwIterations;
     if (write(*(This->fd), &event, sizeof(event)) == -1) {
@@ -530,16 +532,16 @@ static HRESULT WINAPI LinuxInputEffectImpl_SetParameters(
         LPDIRECTINPUTEFFECT iface,
         LPCDIEFFECT peff,
         DWORD dwFlags)
-{
-    LinuxInputEffectImpl* This = (LinuxInputEffectImpl*)iface;
+{       
+    LinuxInputEffectImpl* This = (LinuxInputEffectImpl*)iface; 
     DWORD type = _typeFromGUID(&This->guid);
     HRESULT retval = DI_OK;
 
-    TRACE("(this=%p,%p,%ld)\n", This, peff, dwFlags);
+    TRACE("(this=%p,%p,%d)\n", This, peff, dwFlags);
 
     _dump_DIEFFECT(peff, &This->guid);
 
-    if ((dwFlags & !DIEP_NORESTART & !DIEP_NODOWNLOAD & !DIEP_START) == 0) {
+    if ((dwFlags & ~DIEP_NORESTART & ~DIEP_NODOWNLOAD & ~DIEP_START) == 0) {
        /* set everything */
        dwFlags = DIEP_AXES | DIEP_DIRECTION | DIEP_DURATION | DIEP_ENVELOPE |
            DIEP_GAIN | DIEP_SAMPLEPERIOD | DIEP_STARTDELAY | DIEP_TRIGGERBUTTON |
@@ -556,7 +558,7 @@ static HRESULT WINAPI LinuxInputEffectImpl_SetParameters(
 
     /* some of this may look funky, but it's 'cause the linux driver and directx have
      * different opinions about which way direction "0" is.  directx has 0 along the x
-     * axis (left), linux has it along the y axis (down). */
+     * axis (left), linux has it along the y axis (down). */ 
     if (dwFlags & DIEP_DIRECTION) {
        if (peff->cAxes == 1) {
            if (peff->dwFlags & DIEFF_CARTESIAN) {
@@ -601,11 +603,11 @@ static HRESULT WINAPI LinuxInputEffectImpl_SetParameters(
         if (This->effect.type == FF_CONSTANT) env = &This->effect.u.constant.envelope;
         else if (This->effect.type == FF_PERIODIC) env = &This->effect.u.periodic.envelope;
         else if (This->effect.type == FF_RAMP) env = &This->effect.u.ramp.envelope;
-        else env = NULL;
+        else env = NULL; 
 
        if (peff->lpEnvelope == NULL) {
            /* if this type had an envelope, reset it
-            * note that length can never be zero, so we set it to something miniscule */
+            * note that length can never be zero, so we set it to something minuscule */
            if (env) {
                env->attack_length = 0x10;
                env->attack_level = 0x7FFF;
@@ -626,7 +628,7 @@ static HRESULT WINAPI LinuxInputEffectImpl_SetParameters(
     /* Gain and Sample Period settings are not supported by the linux
      * event system */
     if (dwFlags & DIEP_GAIN)
-       TRACE("Gain requested but no gain functionality present.\n");
+       This->gain = 0xFFFF * peff->dwGain / 10000;
 
     if (dwFlags & DIEP_SAMPLEPERIOD)
        TRACE("Sample period requested but no sample period functionality present.\n");
@@ -652,7 +654,7 @@ static HRESULT WINAPI LinuxInputEffectImpl_SetParameters(
             LPCDIPERIODIC tsp;
             if (peff->cbTypeSpecificParams != sizeof(DIPERIODIC))
                 return DIERR_INVALIDPARAM;
-            tsp = (LPCDIPERIODIC)(peff->lpvTypeSpecificParams);
+            tsp = peff->lpvTypeSpecificParams;
            This->effect.u.periodic.magnitude = (tsp->dwMagnitude / 10) * 32;
            This->effect.u.periodic.offset = (tsp->lOffset / 10) * 32;
            This->effect.u.periodic.phase = (tsp->dwPhase / 9) * 8; /* == (/ 36 * 32) */
@@ -661,17 +663,17 @@ static HRESULT WINAPI LinuxInputEffectImpl_SetParameters(
             LPCDICONSTANTFORCE tsp;
             if (peff->cbTypeSpecificParams != sizeof(DICONSTANTFORCE))
                 return DIERR_INVALIDPARAM;
-            tsp = (LPCDICONSTANTFORCE)(peff->lpvTypeSpecificParams);
-           This->effect.u.constant.level = (tsp->lMagnitude / 10) * 32;
+            tsp = peff->lpvTypeSpecificParams;
+           This->effect.u.constant.level = (max(min(tsp->lMagnitude, 10000), -10000) / 10) * 32;
        } else if (type == DIEFT_RAMPFORCE) {
             LPCDIRAMPFORCE tsp;
             if (peff->cbTypeSpecificParams != sizeof(DIRAMPFORCE))
                 return DIERR_INVALIDPARAM;
-            tsp = (LPCDIRAMPFORCE)(peff->lpvTypeSpecificParams);
+            tsp = peff->lpvTypeSpecificParams;
            This->effect.u.ramp.start_level = (tsp->lStart / 10) * 32;
            This->effect.u.ramp.end_level = (tsp->lStart / 10) * 32;
        } else if (type == DIEFT_CONDITION) {
-            LPCDICONDITION tsp = (LPCDICONDITION)(peff->lpvTypeSpecificParams);
+            LPCDICONDITION tsp = peff->lpvTypeSpecificParams;
             if (peff->cbTypeSpecificParams == sizeof(DICONDITION)) {
                /* One condition block.  This needs to be rotated to direction,
                 * and expanded to separate x and y conditions. */
@@ -682,7 +684,7 @@ static HRESULT WINAPI LinuxInputEffectImpl_SetParameters(
                 for (i = 0; i < 2; ++i) {
                     This->effect.u.condition[i].center = (int)(factor[i] * (tsp->lOffset / 10) * 32);
                     This->effect.u.condition[i].right_coeff = (int)(factor[i] * (tsp->lPositiveCoefficient / 10) * 32);
-                    This->effect.u.condition[i].left_coeff = (int)(factor[i] * (tsp->lNegativeCoefficient / 10) * 32);
+                    This->effect.u.condition[i].left_coeff = (int)(factor[i] * (tsp->lNegativeCoefficient / 10) * 32); 
                     This->effect.u.condition[i].right_saturation = (int)(factor[i] * (tsp->dwPositiveSaturation / 10) * 32);
                     This->effect.u.condition[i].left_saturation = (int)(factor[i] * (tsp->dwNegativeSaturation / 10) * 32);
                     This->effect.u.condition[i].deadband = (int)(factor[i] * (tsp->lDeadBand / 10) * 32);
@@ -702,7 +704,7 @@ static HRESULT WINAPI LinuxInputEffectImpl_SetParameters(
                 return DIERR_INVALIDPARAM;
            }
        } else {
-           FIXME("Custom force types are not supported\n");
+           FIXME("Custom force types are not supported\n");    
            return DIERR_INVALIDPARAM;
        }
     }
@@ -719,20 +721,9 @@ static HRESULT WINAPI LinuxInputEffectImpl_SetParameters(
        retval = LinuxInputEffectImpl_Start(iface, 1, 0);
     if (retval != DI_OK)
        return retval;
-
     return DI_OK;
-}
-
-static ULONG WINAPI LinuxInputEffectImpl_Release(
-       LPDIRECTINPUTEFFECT iface)
-{
-    LinuxInputEffectImpl *This = (LinuxInputEffectImpl *)iface;
-    ULONG ref = InterlockedDecrement(&(This->ref));
-
-    if (ref == 0)
-        HeapFree(GetProcessHeap(), 0, This);
-    return ref;
-}
+}   
 
 static HRESULT WINAPI LinuxInputEffectImpl_Stop(
         LPDIRECTINPUTEFFECT iface)
@@ -767,6 +758,22 @@ static HRESULT WINAPI LinuxInputEffectImpl_Unload(
     return DI_OK;
 }
 
+static ULONG WINAPI LinuxInputEffectImpl_Release(LPDIRECTINPUTEFFECT iface)
+{
+    LinuxInputEffectImpl *This = (LinuxInputEffectImpl *)iface;
+    ULONG ref = InterlockedDecrement(&(This->ref));
+
+    if (ref == 0)
+    {
+        LinuxInputEffectImpl_Stop(iface);
+        LinuxInputEffectImpl_Unload(iface);
+        list_remove(This->entry);
+        HeapFree(GetProcessHeap(), 0, LIST_ENTRY(This->entry, effect_list_item, entry));
+        HeapFree(GetProcessHeap(), 0, This);
+    }
+    return ref;
+}
+
 /******************************************************************************
  *      LinuxInputEffect
  */
@@ -774,20 +781,22 @@ static HRESULT WINAPI LinuxInputEffectImpl_Unload(
 HRESULT linuxinput_create_effect(
        int* fd,
        REFGUID rguid,
+        struct list *parent_list_entry,
        LPDIRECTINPUTEFFECT* peff)
 {
-    LinuxInputEffectImpl* newEffect = HeapAlloc(GetProcessHeap(),
+    LinuxInputEffectImpl* newEffect = HeapAlloc(GetProcessHeap(), 
        HEAP_ZERO_MEMORY, sizeof(LinuxInputEffectImpl));
     DWORD type = _typeFromGUID(rguid);
 
     newEffect->lpVtbl = &LinuxInputEffectVtbl;
     newEffect->ref = 1;
-    memcpy(&(newEffect->guid), rguid, sizeof(*rguid));
+    newEffect->guid = *rguid;
     newEffect->fd = fd;
+    newEffect->gain = 0xFFFF;
 
     /* set the type.  this cannot be changed over the effect's life. */
     switch (type) {
-       case DIEFT_PERIODIC:
+       case DIEFT_PERIODIC: 
            newEffect->effect.type = FF_PERIODIC;
            if (IsEqualGUID(rguid, &GUID_Sine)) {
                newEffect->effect.u.periodic.waveform = FF_SINE;
@@ -801,13 +810,13 @@ HRESULT linuxinput_create_effect(
                 newEffect->effect.u.periodic.waveform = FF_SAW_DOWN;
            }
            break;
-       case DIEFT_CONSTANTFORCE:
+       case DIEFT_CONSTANTFORCE: 
             newEffect->effect.type = FF_CONSTANT;
            break;
-       case DIEFT_RAMPFORCE:
+       case DIEFT_RAMPFORCE: 
            newEffect->effect.type = FF_RAMP;
            break;
-       case DIEFT_CONDITION:
+       case DIEFT_CONDITION: 
            if (IsEqualGUID(rguid, &GUID_Spring)) {
                newEffect->effect.type = FF_SPRING;
             } else if (IsEqualGUID(rguid, &GUID_Friction)) {
@@ -823,7 +832,7 @@ HRESULT linuxinput_create_effect(
            HeapFree(GetProcessHeap(), 0, newEffect);
            return DIERR_INVALIDPARAM;
        default:
-           FIXME("Unknown force type.\n");
+            FIXME("Unknown force type 0x%x.\n", type);
             HeapFree(GetProcessHeap(), 0, newEffect);
            return DIERR_INVALIDPARAM;
     }
@@ -831,9 +840,11 @@ HRESULT linuxinput_create_effect(
     /* mark as non-uploaded */
     newEffect->effect.id = -1;
 
-    *peff = (LPDIRECTINPUTEFFECT)newEffect;
+    newEffect->entry = parent_list_entry;
 
-    TRACE("Creating linux input system effect (%p) with guid %s\n",
+    *peff = (LPDIRECTINPUTEFFECT)newEffect; 
+
+    TRACE("Creating linux input system effect (%p) with guid %s\n", 
          *peff, _dump_dinput_GUID(rguid));
 
     return DI_OK;
@@ -846,29 +857,29 @@ HRESULT linuxinput_get_info_A(
 {
     DWORD type = _typeFromGUID(rguid);
 
-    TRACE("(%d, %s, %p) type=%ld\n", fd, _dump_dinput_GUID(rguid), info, type);
+    TRACE("(%d, %s, %p) type=%d\n", fd, _dump_dinput_GUID(rguid), info, type);
 
     if (!info) return E_POINTER;
 
     if (info->dwSize != sizeof(DIEFFECTINFOA)) return DIERR_INVALIDPARAM;
 
     info->guid = *rguid;
-
-    info->dwEffType = type;
+    
+    info->dwEffType = type; 
     /* the event device API does not support querying for all these things
      * therefore we assume that we have support for them
      * that's not as dangerous as it sounds, since drivers are allowed to
      * ignore parameters they claim to support anyway */
-    info->dwEffType |= DIEFT_DEADBAND | DIEFT_FFATTACK | DIEFT_FFFADE
+    info->dwEffType |= DIEFT_DEADBAND | DIEFT_FFATTACK | DIEFT_FFFADE 
                     | DIEFT_POSNEGCOEFFICIENTS | DIEFT_POSNEGSATURATION
-                   | DIEFT_SATURATION | DIEFT_STARTDELAY;
+                   | DIEFT_SATURATION | DIEFT_STARTDELAY; 
 
     /* again, assume we have support for everything */
     info->dwStaticParams = DIEP_ALLPARAMS;
     info->dwDynamicParams = info->dwStaticParams;
 
     /* yes, this is windows behavior (print the GUID_Name for name) */
-    strcpy((char*)&(info->tszName), _dump_dinput_GUID(rguid));
+    strcpy(info->tszName, _dump_dinput_GUID(rguid));
 
     return DI_OK;
 }
@@ -880,7 +891,7 @@ HRESULT linuxinput_get_info_W(
 {
     DWORD type = _typeFromGUID(rguid);
 
-    TRACE("(%d, %s, %p) type=%ld\n", fd, _dump_dinput_GUID(rguid), info, type);
+    TRACE("(%d, %s, %p) type=%d\n", fd, _dump_dinput_GUID(rguid), info, type);
 
     if (!info) return E_POINTER;
 
@@ -895,15 +906,15 @@ HRESULT linuxinput_get_info_W(
      * ignore parameters they claim to support anyway */
     info->dwEffType |= DIEFT_DEADBAND | DIEFT_FFATTACK | DIEFT_FFFADE
                     | DIEFT_POSNEGCOEFFICIENTS | DIEFT_POSNEGSATURATION
-                    | DIEFT_SATURATION | DIEFT_STARTDELAY;
+                    | DIEFT_SATURATION | DIEFT_STARTDELAY; 
 
     /* again, assume we have support for everything */
     info->dwStaticParams = DIEP_ALLPARAMS;
     info->dwDynamicParams = info->dwStaticParams;
 
     /* yes, this is windows behavior (print the GUID_Name for name) */
-    MultiByteToWideChar(CP_ACP, 0, _dump_dinput_GUID(rguid), -1,
-                       (WCHAR*)&(info->tszName), MAX_PATH);
+    MultiByteToWideChar(CP_ACP, 0, _dump_dinput_GUID(rguid), -1, 
+                        info->tszName, MAX_PATH);
 
     return DI_OK;
 }
index 6c49427..e9b6916 100644 (file)
@@ -16,7 +16,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 /*
 #include <stdio.h>
 #include <string.h>
 #include <time.h>
-#include <errno.h>
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
 #ifdef HAVE_SYS_TIME_H
 # include <sys/time.h>
 #endif
-#include <sys/fcntl.h>
+#include <fcntl.h>
 #ifdef HAVE_SYS_IOCTL_H
 # include <sys/ioctl.h>
 #endif
 #endif
 #ifdef HAVE_LINUX_JOYSTICK_H
 # include <linux/joystick.h>
+# undef SW_MAX
+#endif
+#ifdef HAVE_SYS_POLL_H
+# include <sys/poll.h>
 #endif
 
 #include "wine/debug.h"
@@ -69,89 +72,100 @@ WINE_DEFAULT_DEBUG_CHANNEL(dinput);
 
 #ifdef HAVE_LINUX_22_JOYSTICK_API
 
-#define JOYDEV "/dev/js"
-
-typedef struct {
-    LONG lMin;
-    LONG lMax;
-    LONG lDeadZone;
-    LONG lSaturation;
-} ObjProps;
-
-typedef struct {
-    LONG lX;
-    LONG lY;
-} POV;
+#define JOYDEV_NEW "/dev/input/js"
+#define JOYDEV_OLD "/dev/js"
 
 typedef struct JoystickImpl JoystickImpl;
 static const IDirectInputDevice8AVtbl JoystickAvt;
 static const IDirectInputDevice8WVtbl JoystickWvt;
 struct JoystickImpl
 {
-        const void                     *lpVtbl;
-        LONG                            ref;
-        GUID                            guid;
-       char                            dev[32];
+        struct IDirectInputDevice2AImpl base;
 
-       /* The 'parent' DInput */
-       IDirectInputImpl               *dinput;
+       char                            dev[32];
 
        /* joystick private */
        int                             joyfd;
        DIJOYSTATE2                     js;             /* wine data */
-       LPDIDATAFORMAT                  user_df;        /* user defined format */
-       DataFormat                      *transform;     /* wine to user format converter */
-       int                             *offsets;       /* object offsets */
        ObjProps                        *props;
-        HANDLE                         hEvent;
-        LPDIDEVICEOBJECTDATA           data_queue;
-        int                            queue_head, queue_tail, queue_len;
-       BOOL                            acquired;
        char                            *name;
        DIDEVCAPS                       devcaps;
        LONG                            deadzone;
        int                             *axis_map;
        int                             axes;
-       int                             buttons;
-       POV                             povs[4];
-       CRITICAL_SECTION                crit;
-       BOOL                            overflow;
+        POINTL                          povs[4];
 };
 
-static GUID DInput_Wine_Joystick_GUID = { /* 9e573ed9-7734-11d2-8d4a-23903fb6bdf7 */
+static const GUID DInput_Wine_Joystick_GUID = { /* 9e573ed9-7734-11d2-8d4a-23903fb6bdf7 */
   0x9e573ed9,
   0x7734,
   0x11d2,
   {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
 };
 
-static void _dump_DIDEVCAPS(LPDIDEVCAPS lpDIDevCaps)
+static void _dump_DIDEVCAPS(const DIDEVCAPS *lpDIDevCaps)
 {
-    TRACE("dwSize: %ld\n", lpDIDevCaps->dwSize);
-    TRACE("dwFlags: %08lx\n",lpDIDevCaps->dwFlags);
-    TRACE("dwDevType: %08lx %s\n", lpDIDevCaps->dwDevType,
+    TRACE("dwSize: %d\n", lpDIDevCaps->dwSize);
+    TRACE("dwFlags: %08x\n", lpDIDevCaps->dwFlags);
+    TRACE("dwDevType: %08x %s\n", lpDIDevCaps->dwDevType,
           lpDIDevCaps->dwDevType == DIDEVTYPE_DEVICE ? "DIDEVTYPE_DEVICE" :
           lpDIDevCaps->dwDevType == DIDEVTYPE_DEVICE ? "DIDEVTYPE_DEVICE" :
           lpDIDevCaps->dwDevType == DIDEVTYPE_MOUSE ? "DIDEVTYPE_MOUSE" :
           lpDIDevCaps->dwDevType == DIDEVTYPE_KEYBOARD ? "DIDEVTYPE_KEYBOARD" :
           lpDIDevCaps->dwDevType == DIDEVTYPE_JOYSTICK ? "DIDEVTYPE_JOYSTICK" :
           lpDIDevCaps->dwDevType == DIDEVTYPE_HID ? "DIDEVTYPE_HID" : "UNKNOWN");
-    TRACE("dwAxes: %ld\n",lpDIDevCaps->dwAxes);
-    TRACE("dwButtons: %ld\n",lpDIDevCaps->dwButtons);
-    TRACE("dwPOVs: %ld\n",lpDIDevCaps->dwPOVs);
+    TRACE("dwAxes: %d\n", lpDIDevCaps->dwAxes);
+    TRACE("dwButtons: %d\n", lpDIDevCaps->dwButtons);
+    TRACE("dwPOVs: %d\n", lpDIDevCaps->dwPOVs);
     if (lpDIDevCaps->dwSize > sizeof(DIDEVCAPS_DX3)) {
-        TRACE("dwFFSamplePeriod: %ld\n",lpDIDevCaps->dwFFSamplePeriod);
-        TRACE("dwFFMinTimeResolution: %ld\n",lpDIDevCaps->dwFFMinTimeResolution);
-        TRACE("dwFirmwareRevision: %ld\n",lpDIDevCaps->dwFirmwareRevision);
-        TRACE("dwHardwareRevision: %ld\n",lpDIDevCaps->dwHardwareRevision);
-        TRACE("dwFFDriverVersion: %ld\n",lpDIDevCaps->dwFFDriverVersion);
+        TRACE("dwFFSamplePeriod: %d\n", lpDIDevCaps->dwFFSamplePeriod);
+        TRACE("dwFFMinTimeResolution: %d\n", lpDIDevCaps->dwFFMinTimeResolution);
+        TRACE("dwFirmwareRevision: %d\n", lpDIDevCaps->dwFirmwareRevision);
+        TRACE("dwHardwareRevision: %d\n", lpDIDevCaps->dwHardwareRevision);
+        TRACE("dwFFDriverVersion: %d\n", lpDIDevCaps->dwFFDriverVersion);
     }
 }
 
+#define MAX_JOYSTICKS 64
+static INT joystick_devices_count = -1;
+static LPSTR joystick_devices[MAX_JOYSTICKS];
+
+static INT find_joystick_devices(void)
+{
+    INT i;
+
+    if (joystick_devices_count != -1) return joystick_devices_count;
+
+    joystick_devices_count = 0;
+    for (i = 0; i < MAX_JOYSTICKS; i++)
+    {
+        CHAR device_name[MAX_PATH], *str;
+        INT len;
+        int fd;
+
+        len = sprintf(device_name, "%s%d", JOYDEV_NEW, i) + 1;
+        if ((fd = open(device_name, O_RDONLY)) < 0)
+        {
+            len = sprintf(device_name, "%s%d", JOYDEV_OLD, i) + 1;
+            if ((fd = open(device_name, O_RDONLY)) < 0) continue;
+        }
+
+        close(fd);
+
+        if (!(str = HeapAlloc(GetProcessHeap(), 0, len))) break;
+        memcpy(str, device_name, len);
+
+        joystick_devices[joystick_devices_count++] = str;
+    }
+
+    return joystick_devices_count;
+}
+
 static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
 {
     int fd = -1;
-    char dev[32];
+
+    if (id >= find_joystick_devices()) return FALSE;
 
     if (dwFlags & DIEDFL_FORCEFEEDBACK) {
         WARN("force feedback not supported\n");
@@ -162,9 +176,9 @@ static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTAN
        ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
        (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
         /* check whether we have a joystick */
-        sprintf(dev, "%s%d", JOYDEV, id);
-        if ((fd = open(dev,O_RDONLY)) < 0) {
-            WARN("open(%s,O_RDONLY) failed: %s\n", dev, strerror(errno));
+        if ((fd = open(joystick_devices[id], O_RDONLY)) < 0)
+        {
+            WARN("open(%s, O_RDONLY) failed: %s\n", joystick_devices[id], strerror(errno));
             return FALSE;
         }
 
@@ -180,7 +194,7 @@ static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTAN
         sprintf(lpddi->tszInstanceName, "Joystick %d", id);
 #if defined(JSIOCGNAME)
         if (ioctl(fd,JSIOCGNAME(sizeof(lpddi->tszProductName)),lpddi->tszProductName) < 0) {
-            WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", dev, strerror(errno));
+            WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", joystick_devices[id], strerror(errno));
             strcpy(lpddi->tszProductName, "Wine Joystick");
         }
 #else
@@ -189,7 +203,7 @@ static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTAN
 
         lpddi->guidFFDriver = GUID_NULL;
         close(fd);
-        TRACE("Enumerating the linux Joystick device: %s (%s)\n", dev, lpddi->tszProductName);
+        TRACE("Enumerating the linux Joystick device: %s (%s)\n", joystick_devices[id], lpddi->tszProductName);
         return TRUE;
     }
 
@@ -200,9 +214,10 @@ static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTAN
 {
     int fd = -1;
     char name[MAX_PATH];
-    char dev[32];
     char friendly[32];
 
+    if (id >= find_joystick_devices()) return FALSE;
+
     if (dwFlags & DIEDFL_FORCEFEEDBACK) {
         WARN("force feedback not supported\n");
         return FALSE;
@@ -212,9 +227,9 @@ static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTAN
        ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
        (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
         /* check whether we have a joystick */
-        sprintf(dev, "%s%d", JOYDEV, id);
-        if ((fd = open(dev,O_RDONLY)) < 0) {
-            WARN("open(%s,O_RDONLY) failed: %s\n", dev, strerror(errno));
+        if ((fd = open(joystick_devices[id], O_RDONLY)) < 0)
+        {
+            WARN("open(%s,O_RDONLY) failed: %s\n", joystick_devices[id], strerror(errno));
             return FALSE;
         }
 
@@ -231,7 +246,7 @@ static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTAN
         MultiByteToWideChar(CP_ACP, 0, friendly, -1, lpddi->tszInstanceName, MAX_PATH);
 #if defined(JSIOCGNAME)
         if (ioctl(fd,JSIOCGNAME(sizeof(name)),name) < 0) {
-            WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", dev, strerror(errno));
+            WARN("ioctl(%s, JSIOCGNAME) failed: %s\n", joystick_devices[id], strerror(errno));
             strcpy(name, "Wine Joystick");
         }
 #else
@@ -240,29 +255,13 @@ static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTAN
         MultiByteToWideChar(CP_ACP, 0, name, -1, lpddi->tszProductName, MAX_PATH);
         lpddi->guidFFDriver = GUID_NULL;
         close(fd);
-        TRACE("Enumerating the linux Joystick device: %s (%s)\n",dev,name);
+        TRACE("Enumerating the linux Joystick device: %s (%s)\n", joystick_devices[id], name);
         return TRUE;
     }
 
     return FALSE;
 }
 
-/*
- * Get a config key from either the app-specific or the default config
- */
-
-inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
-                                    char *buffer, DWORD size )
-{
-    if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE)buffer, &size ))
-        return 0;
-
-    if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE)buffer, &size ))
-        return 0;
-
-    return ERROR_FILE_NOT_FOUND;
-}
-
 /*
  * Setup the dinput options.
  */
@@ -270,111 +269,103 @@ inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
 static HRESULT setup_dinput_options(JoystickImpl * device)
 {
     char buffer[MAX_PATH+16];
-    HKEY hkey, appkey = 0;
-    DWORD len;
+    HKEY hkey, appkey;
+    int tokens = 0;
+    int axis = 0;
+    int pov = 0;
 
     buffer[MAX_PATH]='\0';
 
-    /* @@ Wine registry key: HKCU\Software\Wine\DirectInput */
-    if (RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\DirectInput", &hkey)) hkey = 0;
-
-    len = GetModuleFileNameA( 0, buffer, MAX_PATH );
-    if (len && len < MAX_PATH) {
-        HKEY tmpkey;
-        /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DirectInput */
-        if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey ))
-        {
-            char *p, *appname = buffer;
-            if ((p = strrchr( appname, '/' ))) appname = p + 1;
-            if ((p = strrchr( appname, '\\' ))) appname = p + 1;
-            strcat( appname, "\\DirectInput" );
-            if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
-            RegCloseKey( tmpkey );
-        }
-    }
+    get_app_key(&hkey, &appkey);
 
     /* get options */
 
     if (!get_config_key( hkey, appkey, "DefaultDeadZone", buffer, MAX_PATH )) {
         device->deadzone = atoi(buffer);
-        TRACE("setting default deadzone to: \"%s\" %ld\n", buffer, device->deadzone);
+        TRACE("setting default deadzone to: \"%s\" %d\n", buffer, device->deadzone);
     }
 
+    device->axis_map = HeapAlloc(GetProcessHeap(), 0, device->axes * sizeof(int));
+    if (!device->axis_map) return DIERR_OUTOFMEMORY;
+
     if (!get_config_key( hkey, appkey, device->name, buffer, MAX_PATH )) {
-        int tokens = 0;
-        int axis = 0;
-        int pov = 0;
+        static const char *axis_names[] = {"X", "Y", "Z", "Rx", "Ry", "Rz",
+                                           "Slider1", "Slider2",
+                                           "POV1", "POV2", "POV3", "POV4"};
         const char *delim = ",";
         char * ptr;
         TRACE("\"%s\" = \"%s\"\n", device->name, buffer);
 
-        device->axis_map = HeapAlloc(GetProcessHeap(), 0, device->axes * sizeof(int));
-        if (device->axis_map == 0)
-            return DIERR_OUTOFMEMORY;
-
         if ((ptr = strtok(buffer, delim)) != NULL) {
             do {
-                if (strcmp(ptr, "X") == 0) {
-                    device->axis_map[tokens] = 0;
-                    axis++;
-                } else if (strcmp(ptr, "Y") == 0) {
-                    device->axis_map[tokens] = 1;
-                    axis++;
-                } else if (strcmp(ptr, "Z") == 0) {
-                    device->axis_map[tokens] = 2;
-                    axis++;
-                } else if (strcmp(ptr, "Rx") == 0) {
-                    device->axis_map[tokens] = 3;
-                    axis++;
-                } else if (strcmp(ptr, "Ry") == 0) {
-                    device->axis_map[tokens] = 4;
-                    axis++;
-                } else if (strcmp(ptr, "Rz") == 0) {
-                    device->axis_map[tokens] = 5;
-                    axis++;
-                } else if (strcmp(ptr, "Slider1") == 0) {
-                    device->axis_map[tokens] = 6;
-                    axis++;
-                } else if (strcmp(ptr, "Slider2") == 0) {
-                    device->axis_map[tokens] = 7;
-                    axis++;
-                } else if (strcmp(ptr, "POV1") == 0) {
-                    device->axis_map[tokens++] = 8;
-                    device->axis_map[tokens] = 8;
-                    pov++;
-                } else if (strcmp(ptr, "POV2") == 0) {
-                    device->axis_map[tokens++] = 9;
-                    device->axis_map[tokens] = 9;
-                    pov++;
-                } else if (strcmp(ptr, "POV3") == 0) {
-                    device->axis_map[tokens++] = 10;
-                    device->axis_map[tokens] = 10;
-                    pov++;
-                } else if (strcmp(ptr, "POV4") == 0) {
-                    device->axis_map[tokens++] = 11;
-                    device->axis_map[tokens] = 11;
-                    pov++;
-                } else {
-                    ERR("invalid joystick axis type: %s\n", ptr);
-                    device->axis_map[tokens] = tokens;
-                    axis++;
+                int i;
+
+                for (i = 0; i < sizeof(axis_names) / sizeof(axis_names[0]); i++)
+                    if (!strcmp(ptr, axis_names[i]))
+                    {
+                        if (!strncmp(ptr, "POV", 3))
+                        {
+                            if (pov >= 4)
+                            {
+                                WARN("Only 4 POVs supported - ignoring extra\n");
+                                i = -1;
+                            }
+                            else
+                            {
+                                /* Pov takes two axes */
+                                device->axis_map[tokens++] = i;
+                                pov++;
+                            }
+                        }
+                        else
+                        {
+                            if (axis >= 8)
+                            {
+                                FIXME("Only 8 Axes supported - ignoring extra\n");
+                                i = -1;
+                            }
+                            else
+                                axis++;
+                        }
+                        break;
+                    }
+
+                if (i == sizeof(axis_names) / sizeof(axis_names[0]))
+                {
+                    ERR("invalid joystick axis type: \"%s\"\n", ptr);
+                    i = -1;
                 }
 
+                device->axis_map[tokens] = i;
                 tokens++;
             } while ((ptr = strtok(NULL, delim)) != NULL);
 
-            if (tokens != device->devcaps.dwAxes) {
+            if (tokens != device->axes) {
                 ERR("not all joystick axes mapped: %d axes(%d,%d), %d arguments\n", device->axes, axis, pov,tokens);
                 while (tokens < device->axes) {
-                    device->axis_map[tokens] = tokens;
+                    device->axis_map[tokens] = -1;
                     tokens++;
                 }
             }
         }
-
-        device->devcaps.dwAxes = axis;
-        device->devcaps.dwPOVs = pov;
     }
+    else
+    {
+        for (tokens = 0; tokens < device->axes; tokens++)
+        {
+            if (tokens < 8)
+                device->axis_map[tokens] = axis++;
+            else if (tokens < 16)
+            {
+                device->axis_map[tokens++] = 8 + pov;
+                device->axis_map[tokens  ] = 8 + pov++;
+            }
+            else
+                device->axis_map[tokens] = -1;
+        }
+    }
+    device->devcaps.dwAxes = axis;
+    device->devcaps.dwPOVs = pov;
 
     if (appkey)
         RegCloseKey( appkey );
@@ -385,63 +376,17 @@ static HRESULT setup_dinput_options(JoystickImpl * device)
     return DI_OK;
 }
 
-static void calculate_ids(JoystickImpl* device)
-{
-    int i;
-    int axis = 0;
-    int button = 0;
-    int pov = 0;
-    int axis_base;
-    int pov_base;
-    int button_base;
-
-    /* Make two passes over the format. The first counts the number
-     * for each type and the second sets the id */
-    for (i = 0; i < device->user_df->dwNumObjs; i++) {
-        if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_AXIS)
-            axis++;
-        else if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_POV)
-            pov++;
-        else if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_BUTTON)
-            button++;
-    }
-
-    axis_base = 0;
-    pov_base = axis;
-    button_base = axis + pov;
-
-    axis = 0;
-    button = 0;
-    pov = 0;
-
-    for (i = 0; i < device->user_df->dwNumObjs; i++) {
-        DWORD type = 0;
-        if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_AXIS) {
-            axis++;
-            type = DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) |
-                DIDFT_MAKEINSTANCE(axis + axis_base);
-            TRACE("axis type = 0x%08lx\n", type);
-        } else if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_POV) {
-            pov++;
-            type = DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) |
-                DIDFT_MAKEINSTANCE(pov + pov_base);
-            TRACE("POV type = 0x%08lx\n", type);
-        } else if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_BUTTON) {
-            button++;
-            type = DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) |
-                DIDFT_MAKEINSTANCE(button + button_base);
-            TRACE("button type = 0x%08lx\n", type);
-        }
-        device->user_df->rgodf[i].dwType = type;
-    }
-}
-
-static HRESULT alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *dinput, LPDIRECTINPUTDEVICEA* pdev)
+static HRESULT alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *dinput,
+    LPDIRECTINPUTDEVICEA* pdev, unsigned short index)
 {
     DWORD i;
     JoystickImpl* newDevice;
     char name[MAX_PATH];
     HRESULT hr;
+    LPDIDATAFORMAT df = NULL;
+    int idx = 0;
+
+    TRACE("%s %p %p %p %hu\n", debugstr_guid(rguid), jvt, dinput, pdev, index);
 
     newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
     if (newDevice == 0) {
@@ -450,10 +395,10 @@ static HRESULT alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *di
         return DIERR_OUTOFMEMORY;
     }
 
-    sprintf(newDevice->dev, "%s%d", JOYDEV, rguid->Data3);
-
-    if ((newDevice->joyfd = open(newDevice->dev,O_RDONLY)) < 0) {
-        WARN("open(%s,O_RDONLY) failed: %s\n", newDevice->dev, strerror(errno));
+    if (!lstrcpynA(newDevice->dev, joystick_devices[index], sizeof(newDevice->dev)) ||
+        (newDevice->joyfd = open(newDevice->dev, O_RDONLY)) < 0)
+    {
+        WARN("open(%s, O_RDONLY) failed: %s\n", newDevice->dev, strerror(errno));
         HeapFree(GetProcessHeap(), 0, newDevice);
         return DIERR_DEVICENOTREG;
     }
@@ -479,53 +424,62 @@ static HRESULT alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *di
     }
 #endif
 #ifdef JSIOCGBUTTONS
-    if (ioctl(newDevice->joyfd,JSIOCGBUTTONS,&newDevice->buttons) < 0) {
+    if (ioctl(newDevice->joyfd, JSIOCGBUTTONS, &newDevice->devcaps.dwButtons) < 0) {
         WARN("ioctl(%s,JSIOCGBUTTONS) failed: %s, defauting to 2\n", newDevice->dev, strerror(errno));
-        newDevice->buttons = 2;
+        newDevice->devcaps.dwButtons = 2;
     }
 #endif
 
-    newDevice->lpVtbl = jvt;
-    newDevice->ref = 1;
-    newDevice->dinput = dinput;
-    newDevice->acquired = FALSE;
-    newDevice->overflow = FALSE;
-    CopyMemory(&(newDevice->guid),rguid,sizeof(*rguid));
+    if (newDevice->devcaps.dwButtons > 128)
+    {
+        WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->devcaps.dwButtons);
+        newDevice->devcaps.dwButtons = 128;
+    }
+
+    newDevice->base.lpVtbl = jvt;
+    newDevice->base.ref = 1;
+    newDevice->base.dinput = dinput;
+    newDevice->base.guid = *rguid;
+    InitializeCriticalSection(&newDevice->base.crit);
+    newDevice->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->base.crit");
 
     /* setup_dinput_options may change these */
-    newDevice->deadzone = 5000;
-    newDevice->devcaps.dwButtons = newDevice->buttons;
-    newDevice->devcaps.dwAxes = newDevice->axes;
-    newDevice->devcaps.dwPOVs = 0;
+    newDevice->deadzone = 0;
 
     /* do any user specified configuration */
     hr = setup_dinput_options(newDevice);
     if (hr != DI_OK)
         goto FAILED1;
 
-    if (newDevice->axis_map == 0) {
-        newDevice->axis_map = HeapAlloc(GetProcessHeap(), 0, newDevice->axes * sizeof(int));
-        if (newDevice->axis_map == 0)
-            goto FAILED;
-
-        for (i = 0; i < newDevice->axes; i++)
-            newDevice->axis_map[i] = i;
-    }
+    /* Create copy of default data format */
+    if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED;
+    memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
 
-    /* wine uses DIJOYSTATE2 as it's internal format so copy
-     * the already defined format c_dfDIJoystick2 */
-    newDevice->user_df = HeapAlloc(GetProcessHeap(),0,c_dfDIJoystick2.dwSize);
-    if (newDevice->user_df == 0)
-        goto FAILED;
+    df->dwNumObjs = newDevice->devcaps.dwAxes + newDevice->devcaps.dwPOVs + newDevice->devcaps.dwButtons;
+    if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED;
 
-    CopyMemory(newDevice->user_df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
+    for (i = 0; i < newDevice->axes; i++)
+    {
+        int wine_obj = newDevice->axis_map[i];
 
-    /* copy default objects */
-    newDevice->user_df->rgodf = HeapAlloc(GetProcessHeap(),0,c_dfDIJoystick2.dwNumObjs*c_dfDIJoystick2.dwObjSize);
-    if (newDevice->user_df->rgodf == 0)
-        goto FAILED;
+        if (wine_obj < 0) continue;
 
-    CopyMemory(newDevice->user_df->rgodf,c_dfDIJoystick2.rgodf,c_dfDIJoystick2.dwNumObjs*c_dfDIJoystick2.dwObjSize);
+        memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
+        if (wine_obj < 8)
+            df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
+        else
+        {
+            df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj - 8) | DIDFT_POV;
+            i++; /* POV takes 2 axes */
+        }
+    }
+    for (i = 0; i < newDevice->devcaps.dwButtons; i++)
+    {
+        memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
+        df->rgodf[idx  ].pguid = &GUID_Button;
+        df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
+    }
+    newDevice->base.data_format.wine_df = df;
 
     /* create default properties */
     newDevice->props = HeapAlloc(GetProcessHeap(),0,c_dfDIJoystick2.dwNumObjs*sizeof(ObjProps));
@@ -534,29 +488,19 @@ static HRESULT alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *di
 
     /* initialize default properties */
     for (i = 0; i < c_dfDIJoystick2.dwNumObjs; i++) {
+        newDevice->props[i].lDevMin = -32767;
+        newDevice->props[i].lDevMax = +32767;
         newDevice->props[i].lMin = 0;
         newDevice->props[i].lMax = 0xffff;
         newDevice->props[i].lDeadZone = newDevice->deadzone;   /* % * 1000 */
         newDevice->props[i].lSaturation = 0;
     }
 
-    /* create an offsets array */
-    newDevice->offsets = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,c_dfDIJoystick2.dwNumObjs*sizeof(int));
-    if (newDevice->offsets == 0)
-        goto FAILED;
-
-    /* create the default transform filter */
-    newDevice->transform = create_DataFormat(&c_dfDIJoystick2, newDevice->user_df, newDevice->offsets);
-
-    calculate_ids(newDevice);
-
-    IDirectInputDevice_AddRef((LPDIRECTINPUTDEVICE8A)newDevice->dinput);
-    InitializeCriticalSection(&(newDevice->crit));
-    newDevice->crit.DebugInfo->Spare[0] = (DWORD_PTR)"DINPUT_Mouse";
+    IDirectInput_AddRef((LPDIRECTINPUTDEVICE8A)newDevice->base.dinput);
 
     newDevice->devcaps.dwSize = sizeof(newDevice->devcaps);
     newDevice->devcaps.dwFlags = DIDC_ATTACHED;
-    if (newDevice->dinput->dwVersion >= 0x0800)
+    if (newDevice->base.dinput->dwVersion >= 0x0800)
         newDevice->devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
     else
         newDevice->devcaps.dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
@@ -567,9 +511,9 @@ static HRESULT alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *di
     newDevice->devcaps.dwFFDriverVersion = 0;
 
     if (TRACE_ON(dinput)) {
-        _dump_DIDATAFORMAT(newDevice->user_df);
+        _dump_DIDATAFORMAT(newDevice->base.data_format.wine_df);
        for (i = 0; i < (newDevice->axes); i++)
-           TRACE("axis_map[%ld] = %d\n", i, newDevice->axis_map[i]);
+           TRACE("axis_map[%d] = %d\n", i, newDevice->axis_map[i]);
         _dump_DIDEVCAPS(&newDevice->devcaps);
     }
 
@@ -580,18 +524,22 @@ static HRESULT alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *di
 FAILED:
     hr = DIERR_OUTOFMEMORY;
 FAILED1:
+    if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
+    HeapFree(GetProcessHeap(), 0, df);
+    release_DataFormat(&newDevice->base.data_format);
     HeapFree(GetProcessHeap(),0,newDevice->axis_map);
     HeapFree(GetProcessHeap(),0,newDevice->name);
     HeapFree(GetProcessHeap(),0,newDevice->props);
-    HeapFree(GetProcessHeap(),0,newDevice->user_df->rgodf);
-    HeapFree(GetProcessHeap(),0,newDevice->user_df);
     HeapFree(GetProcessHeap(),0,newDevice);
     *pdev = 0;
 
     return hr;
 }
 
-static BOOL IsJoystickGUID(REFGUID guid)
+/******************************************************************************
+  *     get_joystick_index : Get the joystick index from a given GUID
+  */
+static unsigned short get_joystick_index(REFGUID guid)
 {
     GUID wine_joystick = DInput_Wine_Joystick_GUID;
     GUID dev_guid = *guid;
@@ -599,53 +547,71 @@ static BOOL IsJoystickGUID(REFGUID guid)
     wine_joystick.Data3 = 0;
     dev_guid.Data3 = 0;
 
-    return IsEqualGUID(&wine_joystick, &dev_guid);
+    /* for the standard joystick GUID use index 0 */
+    if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
+
+    /* for the wine joystick GUIDs use the index stored in Data3 */
+    if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3;
+
+    return MAX_JOYSTICKS;
 }
 
 static HRESULT joydev_create_deviceA(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
 {
-  if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
-      (IsJoystickGUID(rguid))) {
-    if ((riid == NULL) ||
-       IsEqualGUID(&IID_IDirectInputDeviceA,riid) ||
-       IsEqualGUID(&IID_IDirectInputDevice2A,riid) ||
-       IsEqualGUID(&IID_IDirectInputDevice7A,riid) ||
-       IsEqualGUID(&IID_IDirectInputDevice8A,riid)) {
-      return alloc_device(rguid, &JoystickAvt, dinput, pdev);
-    } else {
-      WARN("no interface\n");
-      *pdev = 0;
-      return DIERR_NOINTERFACE;
+    unsigned short index;
+
+    TRACE("%p %s %p %p\n",dinput, debugstr_guid(rguid), riid, pdev);
+    find_joystick_devices();
+    *pdev = NULL;
+
+    if ((index = get_joystick_index(rguid)) < MAX_JOYSTICKS &&
+        joystick_devices_count && index < joystick_devices_count)
+    {
+        if ((riid == NULL) ||
+           IsEqualGUID(&IID_IDirectInputDeviceA,  riid) ||
+           IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
+           IsEqualGUID(&IID_IDirectInputDevice7A, riid) ||
+           IsEqualGUID(&IID_IDirectInputDevice8A, riid))
+        {
+            return alloc_device(rguid, &JoystickAvt, dinput, pdev, index);
+        }
+
+        WARN("no interface\n");
+        return DIERR_NOINTERFACE;
     }
-  }
 
-  WARN("invalid device GUID\n");
-  *pdev = 0;
-  return DIERR_DEVICENOTREG;
+    return DIERR_DEVICENOTREG;
 }
 
 static HRESULT joydev_create_deviceW(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEW* pdev)
 {
-  if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
-      (IsJoystickGUID(rguid))) {
-    if ((riid == NULL) ||
-       IsEqualGUID(&IID_IDirectInputDeviceW,riid) ||
-       IsEqualGUID(&IID_IDirectInputDevice2W,riid) ||
-       IsEqualGUID(&IID_IDirectInputDevice7W,riid) ||
-       IsEqualGUID(&IID_IDirectInputDevice8W,riid)) {
-      return alloc_device(rguid, &JoystickWvt, dinput, (LPDIRECTINPUTDEVICEA *)pdev);
-    } else {
-      WARN("no interface\n");
-      *pdev = 0;
-      return DIERR_NOINTERFACE;
+    unsigned short index;
+
+    TRACE("%p %s %p %p\n",dinput, debugstr_guid(rguid), riid, pdev);
+    find_joystick_devices();
+    *pdev = NULL;
+
+    if ((index = get_joystick_index(rguid)) < MAX_JOYSTICKS &&
+        joystick_devices_count && index < joystick_devices_count)
+    {
+        if ((riid == NULL) ||
+           IsEqualGUID(&IID_IDirectInputDeviceW,  riid) ||
+           IsEqualGUID(&IID_IDirectInputDevice2W, riid) ||
+           IsEqualGUID(&IID_IDirectInputDevice7W, riid) ||
+           IsEqualGUID(&IID_IDirectInputDevice8W, riid))
+        {
+            return alloc_device(rguid, &JoystickWvt, dinput, (LPDIRECTINPUTDEVICEA *)pdev, index);
+        }
+        WARN("no interface\n");
+        return DIERR_NOINTERFACE;
     }
-  }
 
-  WARN("invalid device GUID\n");
-  *pdev = 0;
-  return DIERR_DEVICENOTREG;
+    WARN("invalid device GUID %s\n",debugstr_guid(rguid));
+    return DIERR_DEVICENOTREG;
 }
 
+#undef MAX_JOYSTICKS
+
 const struct dinput_device joystick_linux_device = {
   "Wine Linux joystick driver",
   joydev_enum_deviceA,
@@ -654,125 +620,6 @@ const struct dinput_device joystick_linux_device = {
   joydev_create_deviceW
 };
 
-/******************************************************************************
- *     Joystick
- */
-static ULONG WINAPI JoystickAImpl_Release(LPDIRECTINPUTDEVICE8A iface)
-{
-    JoystickImpl *This = (JoystickImpl *)iface;
-    ULONG ref;
-
-    ref = InterlockedDecrement((&This->ref));
-    if (ref)
-        return ref;
-
-    /* Free the device name */
-    HeapFree(GetProcessHeap(),0,This->name);
-
-    /* Free the axis map */
-    HeapFree(GetProcessHeap(),0,This->axis_map);
-
-    /* Free the data queue */
-    HeapFree(GetProcessHeap(),0,This->data_queue);
-
-    /* Free the DataFormat */
-    HeapFree(GetProcessHeap(), 0, This->user_df->rgodf);
-    HeapFree(GetProcessHeap(), 0, This->user_df);
-
-    /* Free the properties */
-    HeapFree(GetProcessHeap(), 0, This->props);
-
-    /* Free the offsets array */
-    HeapFree(GetProcessHeap(),0,This->offsets);
-
-    /* release the data transform filter */
-    release_DataFormat(This->transform);
-
-    This->crit.DebugInfo->Spare[0] = 0;
-    DeleteCriticalSection(&(This->crit));
-    IDirectInputDevice_Release((LPDIRECTINPUTDEVICE8A)This->dinput);
-
-    HeapFree(GetProcessHeap(),0,This);
-    return 0;
-}
-
-/******************************************************************************
-  *   SetDataFormat : the application can choose the format of the data
-  *   the device driver sends back with GetDeviceState.
-  */
-static HRESULT WINAPI JoystickAImpl_SetDataFormat(
-    LPDIRECTINPUTDEVICE8A iface,
-    LPCDIDATAFORMAT df)
-{
-    JoystickImpl *This = (JoystickImpl *)iface;
-    unsigned int i;
-    LPDIDATAFORMAT new_df = 0;
-    LPDIOBJECTDATAFORMAT new_rgodf = 0;
-    ObjProps * new_props = 0;
-
-    TRACE("(%p,%p)\n",This,df);
-
-    if (df == NULL) {
-        WARN("invalid pointer\n");
-        return E_POINTER;
-    }
-
-    if (df->dwSize != sizeof(*df)) {
-        WARN("invalid argument\n");
-        return DIERR_INVALIDPARAM;
-    }
-
-    if (This->acquired) {
-        WARN("acquired\n");
-        return DIERR_ACQUIRED;
-    }
-
-    if (TRACE_ON(dinput))
-        _dump_DIDATAFORMAT(df);
-
-    /* Store the new data format */
-    new_df = HeapAlloc(GetProcessHeap(),0,df->dwSize);
-    if (new_df == 0)
-        goto FAILED;
-
-    new_rgodf = HeapAlloc(GetProcessHeap(),0,df->dwNumObjs*df->dwObjSize);
-    if (new_rgodf == 0)
-        goto FAILED;
-
-    new_props = HeapAlloc(GetProcessHeap(),0,df->dwNumObjs*sizeof(ObjProps));
-    if (new_props == 0)
-        goto FAILED;
-
-    HeapFree(GetProcessHeap(),0,This->user_df);
-    HeapFree(GetProcessHeap(),0,This->user_df->rgodf);
-    HeapFree(GetProcessHeap(),0,This->props);
-    release_DataFormat(This->transform);
-
-    This->user_df = new_df;
-    CopyMemory(This->user_df, df, df->dwSize);
-    This->user_df->rgodf = new_rgodf;
-    CopyMemory(This->user_df->rgodf,df->rgodf,df->dwNumObjs*df->dwObjSize);
-    This->props = new_props;
-    for (i = 0; i < df->dwNumObjs; i++) {
-        This->props[i].lMin = 0;
-        This->props[i].lMax = 0xffff;
-        This->props[i].lDeadZone = 1000;
-        This->props[i].lSaturation = 0;
-    }
-    This->transform = create_DataFormat(&c_dfDIJoystick2, This->user_df, This->offsets);
-
-    calculate_ids(This);
-
-    return DI_OK;
-
-FAILED:
-    WARN("out of memory\n");
-    HeapFree(GetProcessHeap(),0,new_props);
-    HeapFree(GetProcessHeap(),0,new_rgodf);
-    HeapFree(GetProcessHeap(),0,new_df);
-    return DIERR_OUTOFMEMORY;
-}
-
 /******************************************************************************
   *     Acquire : gets exclusive control of the joystick
   */
@@ -782,7 +629,7 @@ static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
 
     TRACE("(%p)\n",This);
 
-    if (This->acquired) {
+    if (This->base.acquired) {
         WARN("already acquired\n");
         return S_FALSE;
     }
@@ -798,7 +645,7 @@ static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
         }
     }
 
-    This->acquired = TRUE;
+    This->base.acquired = 1;
 
     return DI_OK;
 }
@@ -809,88 +656,24 @@ static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
 static HRESULT WINAPI JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
 {
     JoystickImpl *This = (JoystickImpl *)iface;
+    HRESULT res;
 
     TRACE("(%p)\n",This);
 
-    if (!This->acquired) {
-        WARN("not acquired\n");
-        return DIERR_NOTACQUIRED;
-    }
+    if ((res = IDirectInputDevice2AImpl_Unacquire(iface)) != DI_OK) return res;
 
     if (This->joyfd!=-1) {
         TRACE("closing joystick device\n");
         close(This->joyfd);
         This->joyfd = -1;
-        This->acquired = FALSE;
         return DI_OK;
     }
 
-    This->acquired = FALSE;
-
     return DI_NOEFFECT;
 }
 
-static LONG map_axis(JoystickImpl * This, short val, short index)
-{
-    double    fval = val;
-    double    fmin = This->props[index].lMin;
-    double    fmax = This->props[index].lMax;
-    double    fret;
-
-    fret = (((fval + 32767.0) * (fmax - fmin)) / (32767.0*2.0)) + fmin;
-
-    if (fret >= 0.0)
-        fret += 0.5;
-    else
-        fret -= 0.5;
-
-    return fret;
-}
-
-/* convert wine format offset to user format object index */
-static int offset_to_object(JoystickImpl *This, int offset)
-{
-    int i;
-
-    for (i = 0; i < This->user_df->dwNumObjs; i++) {
-        if (This->user_df->rgodf[i].dwOfs == offset)
-            return i;
-    }
-
-    return -1;
-}
-
-static LONG calculate_pov(JoystickImpl *This, int index)
-{
-    if (This->povs[index].lX < 16384) {
-        if (This->povs[index].lY < 16384)
-            This->js.rgdwPOV[index] = 31500;
-        else if (This->povs[index].lY > 49150)
-            This->js.rgdwPOV[index] = 22500;
-        else
-            This->js.rgdwPOV[index] = 27000;
-    } else if (This->povs[index].lX > 49150) {
-        if (This->povs[index].lY < 16384)
-            This->js.rgdwPOV[index] = 4500;
-        else if (This->povs[index].lY > 49150)
-            This->js.rgdwPOV[index] = 13500;
-        else
-            This->js.rgdwPOV[index] = 9000;
-    } else {
-        if (This->povs[index].lY < 16384)
-            This->js.rgdwPOV[index] = 0;
-        else if (This->povs[index].lY > 49150)
-            This->js.rgdwPOV[index] = 18000;
-        else
-            This->js.rgdwPOV[index] = -1;
-    }
-
-    return This->js.rgdwPOV[index];
-}
-
 static void joy_polldev(JoystickImpl *This) {
-    struct timeval tv;
-    fd_set     readfds;
+    struct pollfd plfd;
     struct     js_event jse;
     TRACE("(%p)\n", This);
 
@@ -898,10 +681,14 @@ static void joy_polldev(JoystickImpl *This) {
         WARN("no device\n");
         return;
     }
-    while (1) {
-       memset(&tv,0,sizeof(tv));
-       FD_ZERO(&readfds);FD_SET(This->joyfd,&readfds);
-       if (1>select(This->joyfd+1,&readfds,NULL,NULL,&tv))
+    while (1)
+    {
+        LONG value;
+        int inst_id = -1;
+
+       plfd.fd = This->joyfd;
+       plfd.events = POLLIN;
+       if (poll(&plfd,1,0) != 1)
            return;
        /* we have one event, so we can read */
        if (sizeof(jse)!=read(This->joyfd,&jse,sizeof(jse))) {
@@ -909,82 +696,52 @@ static void joy_polldev(JoystickImpl *This) {
        }
         TRACE("js_event: type 0x%x, number %d, value %d\n",
               jse.type,jse.number,jse.value);
-        if (jse.type & JS_EVENT_BUTTON) {
-            int offset = This->offsets[jse.number + 12];
-            int value = jse.value?0x80:0x00;
+        if (jse.type & JS_EVENT_BUTTON)
+        {
+            if (jse.number >= This->devcaps.dwButtons) return;
 
-            This->js.rgbButtons[jse.number] = value;
-            GEN_EVENT(offset,value,jse.time,(This->dinput->evsequence)++);
-        } else if (jse.type & JS_EVENT_AXIS) {
+            inst_id = DIDFT_MAKEINSTANCE(jse.number) | DIDFT_PSHBUTTON;
+            This->js.rgbButtons[jse.number] = value = jse.value ? 0x80 : 0x00;
+        }
+        else if (jse.type & JS_EVENT_AXIS)
+        {
             int number = This->axis_map[jse.number];   /* wine format object index */
-            if (number < 12) {
-                int offset = This->offsets[number];
-                int index = offset_to_object(This, offset);
-                LONG value = map_axis(This, jse.value, index);
 
-                /* FIXME do deadzone and saturation here */
-
-                TRACE("changing axis %d => %d\n", jse.number, number);
-                switch (number) {
-                case 0:
-                    This->js.lX = value;
-                    break;
-                case 1:
-                    This->js.lY = value;
-                    break;
-                case 2:
-                    This->js.lZ = value;
-                    break;
-                case 3:
-                    This->js.lRx = value;
-                    break;
-                case 4:
-                    This->js.lRy = value;
-                    break;
-                case 5:
-                    This->js.lRz = value;
-                    break;
-                case 6:
-                    This->js.rglSlider[0] = value;
-                    break;
-                case 7:
-                    This->js.rglSlider[1] = value;
-                    break;
-                case 8:
-                    /* FIXME don't go off array */
-                    if (This->axis_map[jse.number + 1] == number)
-                        This->povs[0].lX = value;
-                    else if (This->axis_map[jse.number - 1] == number)
-                        This->povs[0].lY = value;
-                    value = calculate_pov(This, 0);
-                    break;
-                case 9:
-                    if (This->axis_map[jse.number + 1] == number)
-                        This->povs[1].lX = value;
-                    else if (This->axis_map[jse.number - 1] == number)
-                        This->povs[1].lY = value;
-                    value = calculate_pov(This, 1);
-                    break;
-                case 10:
-                    if (This->axis_map[jse.number + 1] == number)
-                        This->povs[2].lX = value;
-                    else if (This->axis_map[jse.number - 1] == number)
-                        This->povs[2].lY = value;
-                    value = calculate_pov(This, 2);
-                    break;
-                case 11:
-                    if (This->axis_map[jse.number + 1] == number)
-                        This->povs[3].lX = value;
-                    else if (This->axis_map[jse.number - 1] == number)
-                        This->povs[3].lY = value;
-                    value = calculate_pov(This, 3);
+            if (number < 0) return;
+            inst_id = DIDFT_MAKEINSTANCE(number) | (number < 8 ? DIDFT_ABSAXIS : DIDFT_POV);
+            value = joystick_map_axis(&This->props[id_to_object(This->base.data_format.wine_df, inst_id)], jse.value);
+
+            TRACE("changing axis %d => %d\n", jse.number, number);
+            switch (number)
+            {
+                case 0: This->js.lX  = value; break;
+                case 1: This->js.lY  = value; break;
+                case 2: This->js.lZ  = value; break;
+                case 3: This->js.lRx = value; break;
+                case 4: This->js.lRy = value; break;
+                case 5: This->js.lRz = value; break;
+                case 6: This->js.rglSlider[0] = value; break;
+                case 7: This->js.rglSlider[1] = value; break;
+                case 8: case 9: case 10: case 11:
+                {
+                    int idx = number - 8;
+
+                    if (jse.number % 2)
+                        This->povs[idx].y = jse.value;
+                    else
+                        This->povs[idx].x = jse.value;
+
+                    This->js.rgdwPOV[idx] = value = joystick_map_pov(&This->povs[idx]);
                     break;
                 }
-
-                GEN_EVENT(offset,value,jse.time,(This->dinput->evsequence)++);
-            } else
-                WARN("axis %d not supported\n", number);
+                default:
+                    WARN("axis %d not supported\n", number);
+            }
         }
+        if (inst_id >= 0)
+            queue_event((LPDIRECTINPUTDEVICE8A)This,
+                        id_to_offset(&This->base.data_format, inst_id),
+                        value, jse.time, This->base.dinput->evsequence++);
     }
 }
 
@@ -999,9 +756,9 @@ static HRESULT WINAPI JoystickAImpl_GetDeviceState(
 {
     JoystickImpl *This = (JoystickImpl *)iface;
 
-    TRACE("(%p,0x%08lx,%p)\n",This,len,ptr);
+    TRACE("(%p,0x%08x,%p)\n", This, len, ptr);
 
-    if (!This->acquired) {
+    if (!This->base.acquired) {
         WARN("not acquired\n");
         return DIERR_NOTACQUIRED;
     }
@@ -1010,105 +767,11 @@ static HRESULT WINAPI JoystickAImpl_GetDeviceState(
     joy_polldev(This);
 
     /* convert and copy data to user supplied buffer */
-    fill_DataFormat(ptr, &This->js, This->transform);
+    fill_DataFormat(ptr, len, &This->js, &This->base.data_format);
 
     return DI_OK;
 }
 
-/******************************************************************************
-  *     GetDeviceData : gets buffered input data.
-  */
-static HRESULT WINAPI JoystickAImpl_GetDeviceData(
-    LPDIRECTINPUTDEVICE8A iface,
-    DWORD dodsize,
-    LPDIDEVICEOBJECTDATA dod,
-    LPDWORD entries,
-    DWORD flags)
-{
-    JoystickImpl *This = (JoystickImpl *)iface;
-    DWORD len;
-    int nqtail;
-    HRESULT hr = DI_OK;
-
-    TRACE("(%p)->(dods=%ld,entries=%ld,fl=0x%08lx)\n",This,dodsize,*entries,flags);
-
-    if (!This->acquired) {
-        WARN("not acquired\n");
-        return DIERR_NOTACQUIRED;
-    }
-
-    EnterCriticalSection(&(This->crit));
-
-    joy_polldev(This);
-
-    len = ((This->queue_head < This->queue_tail) ? This->queue_len : 0)
-        + (This->queue_head - This->queue_tail);
-    if (len > *entries)
-        len = *entries;
-
-    if (dod == NULL) {
-        if (len)
-            TRACE("Application discarding %ld event(s).\n", len);
-
-        *entries = len;
-        nqtail = This->queue_tail + len;
-        while (nqtail >= This->queue_len)
-            nqtail -= This->queue_len;
-    } else {
-        if (dodsize < sizeof(DIDEVICEOBJECTDATA_DX3)) {
-            ERR("Wrong structure size !\n");
-            LeaveCriticalSection(&(This->crit));
-            return DIERR_INVALIDPARAM;
-        }
-
-        if (len)
-            TRACE("Application retrieving %ld event(s).\n", len);
-
-        *entries = 0;
-        nqtail = This->queue_tail;
-        while (len) {
-            /* Copy the buffered data into the application queue */
-            memcpy((char *)dod + *entries * dodsize, This->data_queue + nqtail, dodsize);
-            /* Advance position */
-            nqtail++;
-            if (nqtail >= This->queue_len)
-                nqtail -= This->queue_len;
-            (*entries)++;
-            len--;
-        }
-    }
-
-    if (This->overflow) {
-        hr = DI_BUFFEROVERFLOW;
-        if (!(flags & DIGDD_PEEK)) {
-            This->overflow = FALSE;
-        }
-    }
-
-    if (!(flags & DIGDD_PEEK))
-        This->queue_tail = nqtail;
-
-    LeaveCriticalSection(&(This->crit));
-
-    return hr;
-}
-
-static int find_property(JoystickImpl * This, LPCDIPROPHEADER ph)
-{
-    int i;
-    if (ph->dwHow == DIPH_BYOFFSET) {
-        return offset_to_object(This, ph->dwObj);
-    } else if (ph->dwHow == DIPH_BYID) {
-        for (i = 0; i < This->user_df->dwNumObjs; i++) {
-            if ((This->user_df->rgodf[i].dwType & 0x00ffffff) == (ph->dwObj & 0x00ffffff)) {
-                return i;
-            }
-        }
-    }
-
-    return -1;
-}
-
 /******************************************************************************
   *     SetProperty : change input device properties
   */
@@ -1118,7 +781,7 @@ static HRESULT WINAPI JoystickAImpl_SetProperty(
     LPCDIPROPHEADER ph)
 {
     JoystickImpl *This = (JoystickImpl *)iface;
-    int i;
+    DWORD i;
 
     TRACE("(%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
 
@@ -1132,29 +795,18 @@ static HRESULT WINAPI JoystickAImpl_SetProperty(
 
     if (!HIWORD(rguid)) {
         switch (LOWORD(rguid)) {
-        case (DWORD) DIPROP_BUFFERSIZE: {
-            LPCDIPROPDWORD     pd = (LPCDIPROPDWORD)ph;
-            TRACE("buffersize = %ld\n",pd->dwData);
-            if (This->data_queue)
-                This->data_queue = HeapReAlloc(GetProcessHeap(),0, This->data_queue, pd->dwData * sizeof(DIDEVICEOBJECTDATA));
-            else
-                This->data_queue = HeapAlloc(GetProcessHeap(),0, pd->dwData * sizeof(DIDEVICEOBJECTDATA));
-            This->queue_head = 0;
-            This->queue_tail = 0;
-            This->queue_len  = pd->dwData;
-            break;
-        }
-        case (DWORD)DIPROP_RANGE: {
-            LPCDIPROPRANGE     pr = (LPCDIPROPRANGE)ph;
+        case (DWORD_PTR)DIPROP_RANGE: {
+            LPCDIPROPRANGE pr = (LPCDIPROPRANGE)ph;
             if (ph->dwHow == DIPH_DEVICE) {
-                TRACE("proprange(%ld,%ld) all\n",pr->lMin,pr->lMax);
-                for (i = 0; i < This->user_df->dwNumObjs; i++) {
+                TRACE("proprange(%d,%d) all\n", pr->lMin, pr->lMax);
+                for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++) {
                     This->props[i].lMin = pr->lMin;
                     This->props[i].lMax = pr->lMax;
                 }
             } else {
-                int obj = find_property(This, ph);
-                TRACE("proprange(%ld,%ld) obj=%d\n",pr->lMin,pr->lMax,obj);
+                int obj = find_property(&This->base.data_format, ph);
+
+                TRACE("proprange(%d,%d) obj=%d\n", pr->lMin, pr->lMax, obj);
                 if (obj >= 0) {
                     This->props[obj].lMin = pr->lMin;
                     This->props[obj].lMax = pr->lMax;
@@ -1163,15 +815,16 @@ static HRESULT WINAPI JoystickAImpl_SetProperty(
             }
             break;
         }
-        case (DWORD)DIPROP_DEADZONE: {
-            LPCDIPROPDWORD     pd = (LPCDIPROPDWORD)ph;
+        case (DWORD_PTR)DIPROP_DEADZONE: {
+            LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
             if (ph->dwHow == DIPH_DEVICE) {
-                TRACE("deadzone(%ld) all\n",pd->dwData);
-                for (i = 0; i < This->user_df->dwNumObjs; i++)
+                TRACE("deadzone(%d) all\n", pd->dwData);
+                for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++)
                     This->props[i].lDeadZone  = pd->dwData;
             } else {
-                int obj = find_property(This, ph);
-                TRACE("deadzone(%ld) obj=%d\n",pd->dwData,obj);
+                int obj = find_property(&This->base.data_format, ph);
+
+                TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
                 if (obj >= 0) {
                     This->props[obj].lDeadZone  = pd->dwData;
                     return DI_OK;
@@ -1179,15 +832,16 @@ static HRESULT WINAPI JoystickAImpl_SetProperty(
             }
             break;
         }
-        case (DWORD)DIPROP_SATURATION: {
-            LPCDIPROPDWORD     pd = (LPCDIPROPDWORD)ph;
+        case (DWORD_PTR)DIPROP_SATURATION: {
+            LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
             if (ph->dwHow == DIPH_DEVICE) {
-                TRACE("saturation(%ld) all\n",pd->dwData);
-                for (i = 0; i < This->user_df->dwNumObjs; i++)
+                TRACE("saturation(%d) all\n", pd->dwData);
+                for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++)
                     This->props[i].lSaturation = pd->dwData;
             } else {
-                int obj = find_property(This, ph);
-                TRACE("saturation(%ld) obj=%d\n",pd->dwData,obj);
+                int obj = find_property(&This->base.data_format, ph);
+
+                TRACE("saturation(%d) obj=%d\n", pd->dwData, obj);
                 if (obj >= 0) {
                     This->props[obj].lSaturation = pd->dwData;
                     return DI_OK;
@@ -1196,27 +850,13 @@ static HRESULT WINAPI JoystickAImpl_SetProperty(
             break;
         }
         default:
-            FIXME("Unknown type %p (%s)\n",rguid,debugstr_guid(rguid));
-            break;
+            return IDirectInputDevice2AImpl_SetProperty(iface, rguid, ph);
         }
     }
 
     return DI_OK;
 }
 
-/******************************************************************************
-  *     SetEventNotification : specifies event to be sent on state change
-  */
-static HRESULT WINAPI JoystickAImpl_SetEventNotification(
-       LPDIRECTINPUTDEVICE8A iface, HANDLE hnd
-) {
-    JoystickImpl *This = (JoystickImpl *)iface;
-
-    TRACE("(this=%p,%p)\n",This,hnd);
-    This->hEvent = hnd;
-    return DI_OK;
-}
-
 static HRESULT WINAPI JoystickAImpl_GetCapabilities(
        LPDIRECTINPUTDEVICE8A iface,
        LPDIDEVCAPS lpDIDevCaps)
@@ -1253,7 +893,7 @@ static HRESULT WINAPI JoystickAImpl_Poll(LPDIRECTINPUTDEVICE8A iface)
 
     TRACE("(%p)\n",This);
 
-    if (!This->acquired) {
+    if (!This->base.acquired) {
         WARN("not acquired\n");
         return DIERR_NOTACQUIRED;
     }
@@ -1262,155 +902,6 @@ static HRESULT WINAPI JoystickAImpl_Poll(LPDIRECTINPUTDEVICE8A iface)
     return DI_OK;
 }
 
-/******************************************************************************
-  *     EnumObjects : enumerate the different buttons and axis...
-  */
-static HRESULT WINAPI JoystickAImpl_EnumObjects(
-       LPDIRECTINPUTDEVICE8A iface,
-       LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback,
-       LPVOID lpvRef,
-       DWORD dwFlags)
-{
-  JoystickImpl *This = (JoystickImpl *)iface;
-  DIDEVICEOBJECTINSTANCEA ddoi;
-  BYTE i;
-  int user_offset;
-  int user_object;
-
-  TRACE("(this=%p,%p,%p,%08lx)\n", This, lpCallback, lpvRef, dwFlags);
-  if (TRACE_ON(dinput)) {
-    TRACE("  - flags = ");
-    _dump_EnumObjects_flags(dwFlags);
-    TRACE("\n");
-  }
-
-  /* Only the fields till dwFFMaxForce are relevant */
-  ddoi.dwSize = FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA, dwFFMaxForce);
-
-  /* For the joystick, do as is done in the GetCapabilities function */
-  if ((dwFlags == DIDFT_ALL) ||
-      (dwFlags & DIDFT_AXIS) ||
-      (dwFlags & DIDFT_POV)) {
-    int        pov[4] = { 0, 0, 0, 0 };
-    int axes = 0;
-    int povs = 0;
-
-    for (i = 0; i < This->axes; i++) {
-      int wine_obj = This->axis_map[i];
-      BOOL skip = FALSE;
-
-      switch (wine_obj) {
-      case 0:
-       ddoi.guidType = GUID_XAxis;
-       break;
-      case 1:
-       ddoi.guidType = GUID_YAxis;
-       break;
-      case 2:
-       ddoi.guidType = GUID_ZAxis;
-       break;
-      case 3:
-       ddoi.guidType = GUID_RxAxis;
-       break;
-      case 4:
-       ddoi.guidType = GUID_RyAxis;
-       break;
-      case 5:
-       ddoi.guidType = GUID_RzAxis;
-       break;
-      case 6:
-       ddoi.guidType = GUID_Slider;
-       break;
-      case 7:
-       ddoi.guidType = GUID_Slider;
-       break;
-      case 8:
-        pov[0]++;
-       ddoi.guidType = GUID_POV;
-       break;
-      case 9:
-        pov[1]++;
-       ddoi.guidType = GUID_POV;
-       break;
-      case 10:
-        pov[2]++;
-       ddoi.guidType = GUID_POV;
-       break;
-      case 11:
-        pov[3]++;
-       ddoi.guidType = GUID_POV;
-       break;
-      default:
-       ddoi.guidType = GUID_Unknown;
-      }
-      if (wine_obj < 8) {
-          user_offset = This->offsets[wine_obj];       /* get user offset from wine index */
-          user_object = offset_to_object(This, user_offset);
-
-          ddoi.dwType = This->user_df->rgodf[user_object].dwType & 0x00ffffff;
-          ddoi.dwOfs =  This->user_df->rgodf[user_object].dwOfs;
-          sprintf(ddoi.tszName, "Axis %d", axes);
-          axes++;
-      } else {
-          if (pov[wine_obj - 8] < 2) {
-              user_offset = This->offsets[wine_obj];   /* get user offset from wine index */
-              user_object = offset_to_object(This, user_offset);
-
-              ddoi.dwType = This->user_df->rgodf[user_object].dwType & 0x00ffffff;
-              ddoi.dwOfs =  This->user_df->rgodf[user_object].dwOfs;
-              sprintf(ddoi.tszName, "POV %d", povs);
-              povs++;
-          } else
-              skip = TRUE;
-      }
-      if (!skip) {
-          _dump_OBJECTINSTANCEA(&ddoi);
-          if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE)
-              return DI_OK;
-      }
-    }
-  }
-
-  if ((dwFlags == DIDFT_ALL) ||
-      (dwFlags & DIDFT_BUTTON)) {
-
-    /* The DInput SDK says that GUID_Button is only for mouse buttons but well... */
-    ddoi.guidType = GUID_Button;
-
-    for (i = 0; i < This->buttons; i++) {
-      user_offset = This->offsets[i + 12];     /* get user offset from wine index */
-      user_object = offset_to_object(This, user_offset);
-      ddoi.guidType = GUID_Button;
-      ddoi.dwType = This->user_df->rgodf[user_object].dwType & 0x00ffffff;
-      ddoi.dwOfs =  This->user_df->rgodf[user_object].dwOfs;
-      sprintf(ddoi.tszName, "Button %d", i);
-      _dump_OBJECTINSTANCEA(&ddoi);
-      if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) return DI_OK;
-    }
-  }
-
-  return DI_OK;
-}
-
-/******************************************************************************
-  *     EnumObjects : enumerate the different buttons and axis...
-  */
-static HRESULT WINAPI JoystickWImpl_EnumObjects(
-       LPDIRECTINPUTDEVICE8W iface,
-       LPDIENUMDEVICEOBJECTSCALLBACKW lpCallback,
-       LPVOID lpvRef,
-       DWORD dwFlags)
-{
-  JoystickImpl *This = (JoystickImpl *)iface;
-
-  device_enumobjects_AtoWcb_data data;
-
-  data.lpCallBack = lpCallback;
-  data.lpvRef = lpvRef;
-
-  return JoystickAImpl_EnumObjects((LPDIRECTINPUTDEVICE8A) This, (LPDIENUMDEVICEOBJECTSCALLBACKA) DIEnumDevicesCallbackAtoW, (LPVOID) &data, dwFlags);
-}
-
 /******************************************************************************
   *     GetProperty : get input device properties
   */
@@ -1428,48 +919,44 @@ static HRESULT WINAPI JoystickAImpl_GetProperty(
 
     if (!HIWORD(rguid)) {
         switch (LOWORD(rguid)) {
-        case (DWORD) DIPROP_BUFFERSIZE: {
-            LPDIPROPDWORD      pd = (LPDIPROPDWORD)pdiph;
-            TRACE(" return buffersize = %d\n",This->queue_len);
-            pd->dwData = This->queue_len;
-            break;
-        }
-        case (DWORD) DIPROP_RANGE: {
-            LPDIPROPRANGE pr = (LPDIPROPRANGE) pdiph;
-            int obj = find_property(This, pdiph);
+        case (DWORD_PTR) DIPROP_RANGE: {
+            LPDIPROPRANGE pr = (LPDIPROPRANGE)pdiph;
+            int obj = find_property(&This->base.data_format, pdiph);
+
             /* The app is querying the current range of the axis
              * return the lMin and lMax values */
             if (obj >= 0) {
                 pr->lMin = This->props[obj].lMin;
                 pr->lMax = This->props[obj].lMax;
-                TRACE("range(%ld, %ld) obj=%d\n", pr->lMin, pr->lMax, obj);
+                TRACE("range(%d, %d) obj=%d\n", pr->lMin, pr->lMax, obj);
                 return DI_OK;
             }
             break;
         }
-        case (DWORD) DIPROP_DEADZONE: {
-            LPDIPROPDWORD      pd = (LPDIPROPDWORD)pdiph;
-            int obj = find_property(This, pdiph);
+        case (DWORD_PTR) DIPROP_DEADZONE: {
+            LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
+            int obj = find_property(&This->base.data_format, pdiph);
+
             if (obj >= 0) {
                 pd->dwData = This->props[obj].lDeadZone;
-                TRACE("deadzone(%ld) obj=%d\n", pd->dwData, obj);
+                TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
                 return DI_OK;
             }
             break;
         }
-        case (DWORD) DIPROP_SATURATION: {
-            LPDIPROPDWORD      pd = (LPDIPROPDWORD)pdiph;
-            int obj = find_property(This, pdiph);
+        case (DWORD_PTR) DIPROP_SATURATION: {
+            LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
+            int obj = find_property(&This->base.data_format, pdiph);
+
             if (obj >= 0) {
                 pd->dwData = This->props[obj].lSaturation;
-                TRACE("saturation(%ld) obj=%d\n", pd->dwData, obj);
+                TRACE("saturation(%d) obj=%d\n", pd->dwData, obj);
                 return DI_OK;
             }
             break;
         }
         default:
-            FIXME("Unknown type %p (%s)\n",rguid,debugstr_guid(rguid));
-            break;
+            return IDirectInputDevice2AImpl_GetProperty(iface, rguid, pdiph);
         }
     }
 
@@ -1479,95 +966,52 @@ static HRESULT WINAPI JoystickAImpl_GetProperty(
 /******************************************************************************
   *     GetObjectInfo : get object info
   */
-HRESULT WINAPI JoystickAImpl_GetObjectInfo(
-        LPDIRECTINPUTDEVICE8A iface,
-        LPDIDEVICEOBJECTINSTANCEA pdidoi,
-        DWORD dwObj,
-        DWORD dwHow)
+static HRESULT WINAPI JoystickWImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8W iface,
+        LPDIDEVICEOBJECTINSTANCEW pdidoi, DWORD dwObj, DWORD dwHow)
 {
-    JoystickImpl *This = (JoystickImpl *)iface;
-    DIDEVICEOBJECTINSTANCEA didoiA;
-    unsigned int i;
-
-    TRACE("(%p,%p,%ld,0x%08lx(%s))\n",
-          iface, pdidoi, dwObj, dwHow,
-          dwHow == DIPH_BYOFFSET ? "DIPH_BYOFFSET" :
-          dwHow == DIPH_BYID ? "DIPH_BYID" :
-          dwHow == DIPH_BYUSAGE ? "DIPH_BYUSAGE" :
-          "UNKNOWN");
-
-    if (pdidoi == NULL) {
-        WARN("invalid parameter: pdidoi = NULL\n");
-        return DIERR_INVALIDPARAM;
-    }
-
-    if ((pdidoi->dwSize != sizeof(DIDEVICEOBJECTINSTANCEA)) &&
-        (pdidoi->dwSize != sizeof(DIDEVICEOBJECTINSTANCE_DX3A))) {
-        WARN("invalid parameter: pdidoi->dwSize = %ld != %d or %d\n",
-             pdidoi->dwSize, sizeof(DIDEVICEOBJECTINSTANCEA),
-             sizeof(DIDEVICEOBJECTINSTANCE_DX3A));
-        return DIERR_INVALIDPARAM;
-    }
+    static const WCHAR axisW[] = {'A','x','i','s',' ','%','d',0};
+    static const WCHAR povW[] = {'P','O','V',' ','%','d',0};
+    static const WCHAR buttonW[] = {'B','u','t','t','o','n',' ','%','d',0};
+    HRESULT res;
+
+    res = IDirectInputDevice2WImpl_GetObjectInfo(iface, pdidoi, dwObj, dwHow);
+    if (res != DI_OK) return res;
+
+    if      (pdidoi->dwType & DIDFT_AXIS)
+        sprintfW(pdidoi->tszName, axisW, DIDFT_GETINSTANCE(pdidoi->dwType));
+    else if (pdidoi->dwType & DIDFT_POV)
+        sprintfW(pdidoi->tszName, povW, DIDFT_GETINSTANCE(pdidoi->dwType));
+    else if (pdidoi->dwType & DIDFT_BUTTON)
+        sprintfW(pdidoi->tszName, buttonW, DIDFT_GETINSTANCE(pdidoi->dwType));
+
+    _dump_OBJECTINSTANCEW(pdidoi);
+    return res;
+}
 
-    ZeroMemory(&didoiA, sizeof(didoiA));
-    didoiA.dwSize = pdidoi->dwSize;
-
-    switch (dwHow) {
-    case DIPH_BYOFFSET: {
-        int axis = 0;
-        int pov = 0;
-        int button = 0;
-        for (i = 0; i < This->user_df->dwNumObjs; i++) {
-            if (This->user_df->rgodf[i].dwOfs == dwObj) {
-                if (This->user_df->rgodf[i].pguid)
-                    didoiA.guidType = *This->user_df->rgodf[i].pguid;
-                else
-                    didoiA.guidType = GUID_NULL;
-
-                didoiA.dwOfs = dwObj;
-                didoiA.dwType = This->user_df->rgodf[i].dwType;
-                didoiA.dwFlags = This->user_df->rgodf[i].dwFlags;
-
-                if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_AXIS)
-                    sprintf(didoiA.tszName, "Axis %d", axis);
-                else if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_POV)
-                    sprintf(didoiA.tszName, "POV %d", pov);
-                else if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_BUTTON)
-                    sprintf(didoiA.tszName, "Button %d", button);
-
-                CopyMemory(pdidoi, &didoiA, pdidoi->dwSize);
-                return DI_OK;
-            }
+static HRESULT WINAPI JoystickAImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8A iface,
+        LPDIDEVICEOBJECTINSTANCEA pdidoi, DWORD dwObj, DWORD dwHow)
+{
+    HRESULT res;
+    DIDEVICEOBJECTINSTANCEW didoiW;
+    DWORD dwSize = pdidoi->dwSize;
 
-            if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_AXIS)
-                axis++;
-            else if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_POV)
-                pov++;
-            else if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_BUTTON)
-                button++;
-        }
-        break;
-    }
-    case DIPH_BYID:
-        FIXME("dwHow = DIPH_BYID not implemented\n");
-        break;
-    case DIPH_BYUSAGE:
-        FIXME("dwHow = DIPH_BYUSAGE not implemented\n");
-        break;
-    default:
-        WARN("invalid parameter: dwHow = %08lx\n", dwHow);
-        return DIERR_INVALIDPARAM;
-    }
+    didoiW.dwSize = sizeof(didoiW);
+    res = JoystickWImpl_GetObjectInfo((LPDIRECTINPUTDEVICE8W)iface, &didoiW, dwObj, dwHow);
+    if (res != DI_OK) return res;
 
-    CopyMemory(pdidoi, &didoiA, pdidoi->dwSize);
+    memset(pdidoi, 0, pdidoi->dwSize);
+    memcpy(pdidoi, &didoiW, FIELD_OFFSET(DIDEVICEOBJECTINSTANCEW, tszName));
+    pdidoi->dwSize = dwSize;
+    WideCharToMultiByte(CP_ACP, 0, didoiW.tszName, -1, pdidoi->tszName,
+                        sizeof(pdidoi->tszName), NULL, NULL);
 
-    return DI_OK;
+    return res;
 }
 
 /******************************************************************************
   *     GetDeviceInfo : get information about a device's identity
   */
-HRESULT WINAPI JoystickAImpl_GetDeviceInfo(
+static HRESULT WINAPI JoystickAImpl_GetDeviceInfo(
     LPDIRECTINPUTDEVICE8A iface,
     LPDIDEVICEINSTANCEA pdidi)
 {
@@ -1582,9 +1026,7 @@ HRESULT WINAPI JoystickAImpl_GetDeviceInfo(
 
     if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3A)) &&
         (pdidi->dwSize != sizeof(DIDEVICEINSTANCEA))) {
-        WARN("invalid parameter: pdidi->dwSize = %ld != %d or %d\n",
-             pdidi->dwSize, sizeof(DIDEVICEINSTANCE_DX3A),
-             sizeof(DIDEVICEINSTANCEA));
+        WARN("invalid parameter: pdidi->dwSize = %d\n", pdidi->dwSize);
         return DIERR_INVALIDPARAM;
     }
 
@@ -1607,7 +1049,7 @@ HRESULT WINAPI JoystickAImpl_GetDeviceInfo(
 /******************************************************************************
   *     GetDeviceInfo : get information about a device's identity
   */
-HRESULT WINAPI JoystickWImpl_GetDeviceInfo(
+static HRESULT WINAPI JoystickWImpl_GetDeviceInfo(
     LPDIRECTINPUTDEVICE8W iface,
     LPDIDEVICEINSTANCEW pdidi)
 {
@@ -1617,9 +1059,7 @@ HRESULT WINAPI JoystickWImpl_GetDeviceInfo(
 
     if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3W)) &&
         (pdidi->dwSize != sizeof(DIDEVICEINSTANCEW))) {
-        WARN("invalid parameter: pdidi->dwSize = %ld != %d or %d\n",
-             pdidi->dwSize, sizeof(DIDEVICEINSTANCE_DX3W),
-             sizeof(DIDEVICEINSTANCEW));
+        WARN("invalid parameter: pdidi->dwSize = %d\n", pdidi->dwSize);
         return DIERR_INVALIDPARAM;
     }
 
@@ -1643,17 +1083,17 @@ static const IDirectInputDevice8AVtbl JoystickAvt =
 {
        IDirectInputDevice2AImpl_QueryInterface,
        IDirectInputDevice2AImpl_AddRef,
-       JoystickAImpl_Release,
+        IDirectInputDevice2AImpl_Release,
        JoystickAImpl_GetCapabilities,
-       JoystickAImpl_EnumObjects,
+        IDirectInputDevice2AImpl_EnumObjects,
        JoystickAImpl_GetProperty,
        JoystickAImpl_SetProperty,
        JoystickAImpl_Acquire,
        JoystickAImpl_Unacquire,
        JoystickAImpl_GetDeviceState,
-       JoystickAImpl_GetDeviceData,
-       JoystickAImpl_SetDataFormat,
-       JoystickAImpl_SetEventNotification,
+       IDirectInputDevice2AImpl_GetDeviceData,
+       IDirectInputDevice2AImpl_SetDataFormat,
+       IDirectInputDevice2AImpl_SetEventNotification,
        IDirectInputDevice2AImpl_SetCooperativeLevel,
        JoystickAImpl_GetObjectInfo,
        JoystickAImpl_GetDeviceInfo,
@@ -1676,28 +1116,28 @@ static const IDirectInputDevice8AVtbl JoystickAvt =
 };
 
 #if !defined(__STRICT_ANSI__) && defined(__GNUC__)
-# define XCAST(fun)    (typeof(SysJoystickWvt.fun))
+# define XCAST(fun)    (typeof(JoystickWvt.fun))
 #else
 # define XCAST(fun)    (void*)
 #endif
 
-static const IDirectInputDevice8WVtbl SysJoystickWvt =
+static const IDirectInputDevice8WVtbl JoystickWvt =
 {
        IDirectInputDevice2WImpl_QueryInterface,
        XCAST(AddRef)IDirectInputDevice2AImpl_AddRef,
-       XCAST(Release)JoystickAImpl_Release,
+        XCAST(Release)IDirectInputDevice2AImpl_Release,
        XCAST(GetCapabilities)JoystickAImpl_GetCapabilities,
-       JoystickWImpl_EnumObjects,
+        IDirectInputDevice2WImpl_EnumObjects,
        XCAST(GetProperty)JoystickAImpl_GetProperty,
        XCAST(SetProperty)JoystickAImpl_SetProperty,
        XCAST(Acquire)JoystickAImpl_Acquire,
        XCAST(Unacquire)JoystickAImpl_Unacquire,
        XCAST(GetDeviceState)JoystickAImpl_GetDeviceState,
-       XCAST(GetDeviceData)JoystickAImpl_GetDeviceData,
-       XCAST(SetDataFormat)JoystickAImpl_SetDataFormat,
-       XCAST(SetEventNotification)JoystickAImpl_SetEventNotification,
+       XCAST(GetDeviceData)IDirectInputDevice2AImpl_GetDeviceData,
+       XCAST(SetDataFormat)IDirectInputDevice2AImpl_SetDataFormat,
+       XCAST(SetEventNotification)IDirectInputDevice2AImpl_SetEventNotification,
        XCAST(SetCooperativeLevel)IDirectInputDevice2AImpl_SetCooperativeLevel,
-       IDirectInputDevice2WImpl_GetObjectInfo,
+        JoystickWImpl_GetObjectInfo,
        JoystickWImpl_GetDeviceInfo,
        XCAST(RunControlPanel)IDirectInputDevice2AImpl_RunControlPanel,
        XCAST(Initialize)IDirectInputDevice2AImpl_Initialize,
index 9b494b8..e1b92f7 100644 (file)
@@ -17,7 +17,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #include "config.h"
@@ -34,7 +34,7 @@
 #ifdef HAVE_SYS_TIME_H
 # include <sys/time.h>
 #endif
-#include <sys/fcntl.h>
+#include <fcntl.h>
 #ifdef HAVE_SYS_IOCTL_H
 # include <sys/ioctl.h>
 #endif
 #endif
 #ifdef HAVE_LINUX_INPUT_H
 # include <linux/input.h>
+# undef SW_MAX
 # if defined(EVIOCGBIT) && defined(EV_ABS) && defined(BTN_PINKIE)
 #  define HAVE_CORRECT_LINUXINPUT_H
 # endif
 #endif
+#ifdef HAVE_SYS_POLL_H
+# include <sys/poll.h>
+#endif
 
 #include "wine/debug.h"
 #include "wine/unicode.h"
+#include "wine/list.h"
 #include "windef.h"
 #include "winbase.h"
 #include "winerror.h"
+#include "winreg.h"
 #include "dinput.h"
 
 #include "dinput_private.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
 
+
+/*
+ * Maps POV x & y event values to a DX "clock" position:
+ *         0
+ *   31500    4500
+ * 27000  -1    9000
+ *   22500   13500
+ *       18000
+ */
+DWORD joystick_map_pov(POINTL *p)
+{
+    if (p->x > 0)
+        return p->y < 0 ?  4500 : !p->y ?  9000 : 13500;
+    else if (p->x < 0)
+        return p->y < 0 ? 31500 : !p->y ? 27000 : 22500;
+    else
+        return p->y < 0 ?     0 : !p->y ?    -1 : 18000;
+}
+
+/*
+ * This maps the read value (from the input event) to a value in the
+ * 'wanted' range.
+ * Notes:
+ *   Dead zone is in % multiplied by a 100 (range 0..10000)
+ */
+LONG joystick_map_axis(ObjProps *props, int val)
+{
+    LONG ret;
+    LONG dead_zone = MulDiv( props->lDeadZone, props->lDevMax - props->lDevMin, 10000 );
+    LONG dev_range = props->lDevMax - props->lDevMin - dead_zone;
+
+    /* Center input */
+    val -= (props->lDevMin + props->lDevMax) / 2;
+
+    /* Remove dead zone */
+    if (abs( val ) <= dead_zone / 2)
+        val = 0;
+    else
+        val = val < 0 ? val + dead_zone / 2 : val - dead_zone / 2;
+
+    /* Scale and map the value from the device range into the required range */
+    ret = MulDiv( val, props->lMax - props->lMin, dev_range ) +
+          (props->lMin + props->lMax) / 2;
+
+    /* Clamp in case or rounding errors */
+    if      (ret > props->lMax) ret = props->lMax;
+    else if (ret < props->lMin) ret = props->lMin;
+
+    TRACE( "(%d <%d> %d) -> (%d <%d> %d): val=%d ret=%d\n",
+           props->lDevMin, dead_zone, props->lDevMax,
+           props->lMin, props->lDeadZone, props->lMax,
+           val, ret );
+
+    return ret;
+}
+
 #ifdef HAVE_CORRECT_LINUXINPUT_H
 
 #define EVDEVPREFIX    "/dev/input/event"
 
 /* Wine joystick driver object instances */
-#define WINE_JOYSTICK_AXIS_BASE   0
-#define WINE_JOYSTICK_BUTTON_BASE 8
-
-typedef struct EffectListItem EffectListItem;
-struct EffectListItem
-{
-        LPDIRECTINPUTEFFECT ref;
-       struct EffectListItem* next;
+#define WINE_JOYSTICK_MAX_AXES    8
+#define WINE_JOYSTICK_MAX_POVS    4
+#define WINE_JOYSTICK_MAX_BUTTONS 128
+
+struct wine_input_absinfo {
+    LONG value;
+    LONG minimum;
+    LONG maximum;
+    LONG fuzz;
+    LONG flat;
 };
 
 /* implemented in effect_linuxinput.c */
-HRESULT linuxinput_create_effect(int* fd, REFGUID rguid, LPDIRECTINPUTEFFECT* peff);
+HRESULT linuxinput_create_effect(int* fd, REFGUID rguid, struct list *parent_list_entry, LPDIRECTINPUTEFFECT* peff);
 HRESULT linuxinput_get_info_A(int fd, REFGUID rguid, LPDIEFFECTINFOA info);
 HRESULT linuxinput_get_info_W(int fd, REFGUID rguid, LPDIEFFECTINFOW info);
 
 typedef struct JoystickImpl JoystickImpl;
 static const IDirectInputDevice8AVtbl JoystickAvt;
 static const IDirectInputDevice8WVtbl JoystickWvt;
-struct JoystickImpl
-{
-        const void                     *lpVtbl;
-        LONG                            ref;
-        GUID                            guid;
 
+struct JoyDev {
+       char *device;
+       char *name;
+       GUID guid;
 
-       /* The 'parent' DInput */
-       IDirectInputImpl               *dinput;
+       int has_ff;
+        int num_effects;
 
-       /* joystick private */
-       /* what range and deadzone the game wants */
-       LONG                            wantmin[ABS_MAX];
-       LONG                            wantmax[ABS_MAX];
-       LONG                            deadz[ABS_MAX];
+       /* data returned by EVIOCGBIT for caps, EV_ABS, EV_KEY, and EV_FF */
+       BYTE                            evbits[(EV_MAX+7)/8];
+       BYTE                            absbits[(ABS_MAX+7)/8];
+       BYTE                            keybits[(KEY_MAX+7)/8];
+       BYTE                            ffbits[(FF_MAX+7)/8];   
 
-       /* autodetecting ranges per axe by following movement */
-       LONG                            havemax[ABS_MAX];
-       LONG                            havemin[ABS_MAX];
+       /* data returned by the EVIOCGABS() ioctl */
+        struct wine_input_absinfo       axes[ABS_MAX];
+};
 
+struct JoystickImpl
+{
+        struct IDirectInputDevice2AImpl base;
+
+        struct JoyDev                  *joydev;
+
+       /* joystick private */
        int                             joyfd;
 
-       LPDIDATAFORMAT                  df;
-        HANDLE                         hEvent;
-        LPDIDEVICEOBJECTDATA           data_queue;
-        int                            queue_head, queue_tail, queue_len;
-       BOOL                            overflow;
        DIJOYSTATE2                     js;
 
-       /* Force feedback variables */
-       BOOL                            has_ff;
-       int                             num_effects;
-       EffectListItem*                 top_effect;
-       int                             ff_state;
+       ObjProps                        props[ABS_MAX];
 
-       /* data returned by the EVIOCGABS() ioctl */
-       int                             axes[ABS_MAX+1][5];
+       int                             axes[ABS_MAX];
+        POINTL                          povs[4];
 
-#define AXE_ABS                0
-#define AXE_ABSMIN     1
-#define AXE_ABSMAX     2
-#define AXE_ABSFUZZ    3
-#define AXE_ABSFLAT    4
+       /* LUT for KEY_ to offset in rgbButtons */
+       BYTE                            buttons[KEY_MAX];
 
+       DWORD                           numAxes;
+       DWORD                           numPOVs;
+       DWORD                           numButtons;
 
-       /* data returned by EVIOCGBIT for caps, EV_ABS, EV_KEY, and EV_FF */
-       BYTE                            evbits[(EV_MAX+7)/8];
-       BYTE                            absbits[(ABS_MAX+7)/8];
-       BYTE                            keybits[(KEY_MAX+7)/8];
-       BYTE                            ffbits[(FF_MAX+7)/8];
+       /* Force feedback variables */
+        struct list                     ff_effects;
+       int                             ff_state;
+       int                             ff_autocenter;
 };
 
+static void fake_current_js_state(JoystickImpl *ji);
+static void find_joydevs(void);
+
 /* This GUID is slightly different from the linux joystick one. Take note. */
-static GUID DInput_Wine_Joystick_GUID = { /* 9e573eda-7734-11d2-8d4a-23903fb6bdf7 */
+static const GUID DInput_Wine_Joystick_Base_GUID = { /* 9e573eda-7734-11d2-8d4a-23903fb6bdf7 */
   0x9e573eda,
   0x7734,
   0x11d2,
   {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
 };
 
-static void fake_current_js_state(JoystickImpl *ji);
-static int find_property_offset(JoystickImpl *This, LPCDIPROPHEADER ph);
+#define test_bit(arr,bit) (((BYTE*)(arr))[(bit)>>3]&(1<<((bit)&7)))
+
+#define MAX_JOYDEV 64
 
-#define test_bit(arr,bit) (((BYTE*)arr)[bit>>3]&(1<<(bit&7)))
+static int have_joydevs = -1;
+static struct JoyDev *joydevs = NULL;
 
-static int joydev_have(BOOL require_ff)
+static void find_joydevs(void)
 {
-  int i, fd, flags, num_effects;
-  int havejoy = 0;
-
-  for (i=0;i<64;i++) {
-      char     buf[200];
-      BYTE     absbits[(ABS_MAX+7)/8],keybits[(KEY_MAX+7)/8];
-      BYTE     evbits[(EV_MAX+7)/8],ffbits[(FF_MAX+7)/8];
-
-      sprintf(buf,EVDEVPREFIX"%d",i);
-
-      if (require_ff)
-         flags = O_RDWR;
-      else
-         flags = O_RDONLY;
-
-      if (-1!=(fd=open(buf,flags))) {
-         if (-1==ioctl(fd,EVIOCGBIT(EV_ABS,sizeof(absbits)),absbits)) {
-             perror("EVIOCGBIT EV_ABS");
-             close(fd);
-             continue;
-         }
-         if (-1==ioctl(fd,EVIOCGBIT(EV_KEY,sizeof(keybits)),keybits)) {
-             perror("EVIOCGBIT EV_KEY");
-             close(fd);
-             continue;
-         }
-
-         /* test for force feedback if it's required */
-         if (require_ff) {
-             if ((-1==ioctl(fd,EVIOCGBIT(0,sizeof(evbits)),evbits))) {
-                 perror("EVIOCGBIT 0");
-                 close(fd);
-                 continue;
-             }
-             if (   (!test_bit(evbits,EV_FF))
-                 || (-1==ioctl(fd,EVIOCGBIT(EV_FF,sizeof(ffbits)),ffbits))
-                  || (-1==ioctl(fd,EVIOCGEFFECTS,&num_effects))
-                  || (num_effects <= 0)) {
-                 close(fd);
-                 continue;
-             }
-         }
-
-         /* A true joystick has at least axis X and Y, and at least 1
-          * button. copied from linux/drivers/input/joydev.c */
-         if (test_bit(absbits,ABS_X) && test_bit(absbits,ABS_Y) &&
-             (   test_bit(keybits,BTN_TRIGGER) ||
-                 test_bit(keybits,BTN_A)       ||
-                 test_bit(keybits,BTN_1)
-             )
-         ) {
-             FIXME("found a joystick at %s!\n",buf);
-             havejoy = 1;
-         }
-         close(fd);
-      }
-      if (havejoy || (errno==ENODEV))
-         break;
-  }
-  return havejoy;
+    int i;
+
+    if (InterlockedCompareExchange(&have_joydevs, 0, -1) != -1)
+        /* Someone beat us to it */
+        return;
+
+    for (i = 0; i < MAX_JOYDEV; i++)
+    {
+        char buf[MAX_PATH];
+        struct JoyDev joydev = {0};
+        int fd;
+        int no_ff_check = 0;
+        int j;
+        struct JoyDev *new_joydevs;
+
+        snprintf(buf, sizeof(buf), EVDEVPREFIX"%d", i);
+
+        if ((fd = open(buf, O_RDWR)) == -1)
+        {
+            fd = open(buf, O_RDONLY);
+            no_ff_check = 1;
+        }
+
+        if (fd == -1)
+        {
+            WARN("Failed to open \"%s\": %d %s\n", buf, errno, strerror(errno));
+            continue;
+        }
+
+        if (ioctl(fd, EVIOCGBIT(0, sizeof(joydev.evbits)), joydev.evbits) == -1)
+        {
+            WARN("ioct(EVIOCGBIT, 0) failed: %d %s\n", errno, strerror(errno));
+            close(fd);
+            continue;
+        }
+        if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(joydev.absbits)), joydev.absbits) == -1)
+        {
+            WARN("ioct(EVIOCGBIT, EV_ABS) failed: %d %s\n", errno, strerror(errno));
+            close(fd);
+            continue;
+        }
+        if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(joydev.keybits)), joydev.keybits) == -1)
+        {
+            WARN("ioct(EVIOCGBIT, EV_KEY) failed: %d %s\n", errno, strerror(errno));
+            close(fd);
+            continue;
+        }
+
+        /* A true joystick has at least axis X and Y, and at least 1
+         * button. copied from linux/drivers/input/joydev.c */
+        if (!test_bit(joydev.absbits, ABS_X) || !test_bit(joydev.absbits, ABS_Y) ||
+            !(test_bit(joydev.keybits, BTN_TRIGGER) ||
+              test_bit(joydev.keybits, BTN_A) ||
+              test_bit(joydev.keybits, BTN_1)))
+        {
+            close(fd);
+            continue;
+        }
+
+        if (!(joydev.device = HeapAlloc(GetProcessHeap(), 0, strlen(buf) + 1)))
+        {
+            close(fd);
+            continue;
+        }
+        strcpy(joydev.device, buf);
+
+        buf[MAX_PATH - 1] = 0;
+        if (ioctl(fd, EVIOCGNAME(MAX_PATH - 1), buf) != -1 &&
+            (joydev.name = HeapAlloc(GetProcessHeap(), 0, strlen(buf) + 1)))
+            strcpy(joydev.name, buf);
+        else
+            joydev.name = joydev.device;
+
+       joydev.guid = DInput_Wine_Joystick_Base_GUID;
+       joydev.guid.Data3 += have_joydevs;
+
+        TRACE("Found a joystick on %s: %s (%s)\n", 
+            joydev.device, joydev.name, 
+            debugstr_guid(&joydev.guid)
+            );
+
+#ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
+        if (!no_ff_check &&
+            test_bit(joydev.evbits, EV_FF) &&
+            ioctl(fd, EVIOCGBIT(EV_FF, sizeof(joydev.ffbits)), joydev.ffbits) != -1 &&
+            ioctl(fd, EVIOCGEFFECTS, &joydev.num_effects) != -1 &&
+            joydev.num_effects > 0)
+        {
+           TRACE(" ... with force feedback\n");
+           joydev.has_ff = 1;
+        }
+#endif
+
+        for (j = 0; j < ABS_MAX;j ++)
+        {
+            if (!test_bit(joydev.absbits, j)) continue;
+            if (ioctl(fd, EVIOCGABS(j), &(joydev.axes[j])) != -1)
+            {
+             TRACE(" ... with axis %d: cur=%d, min=%d, max=%d, fuzz=%d, flat=%d\n",
+                 j,
+                 joydev.axes[j].value,
+                 joydev.axes[j].minimum,
+                 joydev.axes[j].maximum,
+                 joydev.axes[j].fuzz,
+                 joydev.axes[j].flat
+                 );
+           }
+       }
+
+        if (!have_joydevs)
+            new_joydevs = HeapAlloc(GetProcessHeap(), 0, sizeof(struct JoyDev));
+        else
+            new_joydevs = HeapReAlloc(GetProcessHeap(), 0, joydevs, (1 + have_joydevs) * sizeof(struct JoyDev));
+
+        if (!new_joydevs)
+        {
+            close(fd);
+            continue;
+        }
+        joydevs = new_joydevs;
+        memcpy(joydevs + have_joydevs, &joydev, sizeof(joydev));
+        have_joydevs++;
+
+        close(fd);
+    }
 }
 
-static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
+static void fill_joystick_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
 {
-  int havejoy = 0;
+    DWORD dwSize = lpddi->dwSize;
 
-  if (id != 0)
-      return FALSE;
+    TRACE("%d %p\n", dwSize, lpddi);
+    memset(lpddi, 0, dwSize);
 
-  if (!((dwDevType == 0) ||
-        ((dwDevType == DIDEVTYPE_JOYSTICK) && (version < 0x0800)) ||
-        (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
-    return FALSE;
+    lpddi->dwSize       = dwSize;
+    lpddi->guidInstance = joydevs[id].guid;
+    lpddi->guidProduct  = DInput_Wine_Joystick_Base_GUID;
+    lpddi->guidFFDriver = GUID_NULL;
 
-#ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
-  if (dwFlags & DIEDFL_FORCEFEEDBACK)
-    return FALSE;
-#endif
+    if (version >= 0x0800)
+        lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
+    else
+        lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
 
-  havejoy = joydev_have(dwFlags & DIEDFL_FORCEFEEDBACK);
+    strcpy(lpddi->tszInstanceName, joydevs[id].name);
+    strcpy(lpddi->tszProductName, joydevs[id].device);
+}
 
-  if (!havejoy)
-      return FALSE;
+static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
+{
+    DWORD dwSize = lpddi->dwSize;
 
-  TRACE("Enumerating the linuxinput Joystick device\n");
+    TRACE("%d %p\n", dwSize, lpddi);
+    memset(lpddi, 0, dwSize);
 
-  /* Return joystick */
-  lpddi->guidInstance  = GUID_Joystick;
-  lpddi->guidProduct   = DInput_Wine_Joystick_GUID;
+    lpddi->dwSize       = dwSize;
+    lpddi->guidInstance = joydevs[id].guid;
+    lpddi->guidProduct  = DInput_Wine_Joystick_Base_GUID;
+    lpddi->guidFFDriver = GUID_NULL;
 
-  lpddi->guidFFDriver = GUID_NULL;
-  if (version >= 0x0800)
-    lpddi->dwDevType    = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
-  else
-    lpddi->dwDevType    = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
+    if (version >= 0x0800)
+        lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
+    else
+        lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
 
-  strcpy(lpddi->tszInstanceName, "Joystick");
-  /* ioctl JSIOCGNAME(len) */
-  strcpy(lpddi->tszProductName,        "Wine Joystick");
-  return TRUE;
+    MultiByteToWideChar(CP_ACP, 0, joydevs[id].name, -1, lpddi->tszInstanceName, MAX_PATH);
+    MultiByteToWideChar(CP_ACP, 0, joydevs[id].device, -1, lpddi->tszProductName, MAX_PATH);
 }
 
-static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
+static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
 {
-  int havejoy = 0;
+  find_joydevs();
 
-  if (id != 0)
-      return FALSE;
+  if (id >= have_joydevs) {
+    return FALSE;
+  }
 
   if (!((dwDevType == 0) ||
-        ((dwDevType == DIDEVTYPE_JOYSTICK) && (version < 0x0800)) ||
+        ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
     return FALSE;
 
@@ -270,282 +403,287 @@ static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTAN
     return FALSE;
 #endif
 
-  havejoy = joydev_have(dwFlags & DIEDFL_FORCEFEEDBACK);
+  if (!(dwFlags & DIEDFL_FORCEFEEDBACK) || joydevs[id].has_ff) {
+    fill_joystick_dideviceinstanceA(lpddi, version, id);
+    return TRUE;
+  }
+  return FALSE;
+}
 
-  if (!havejoy)
-      return FALSE;
+static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
+{
+  find_joydevs();
 
-  TRACE("Enumerating the linuxinput Joystick device\n");
+  if (id >= have_joydevs) {
+    return FALSE;
+  }
 
-  /* Return joystick */
-  lpddi->guidInstance  = GUID_Joystick;
-  lpddi->guidProduct   = DInput_Wine_Joystick_GUID;
+  if (!((dwDevType == 0) ||
+        ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
+        (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
+    return FALSE;
 
-  lpddi->guidFFDriver = GUID_NULL;
-  if (version >= 0x0800)
-    lpddi->dwDevType    = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
-  else
-    lpddi->dwDevType    = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
+#ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
+  if (dwFlags & DIEDFL_FORCEFEEDBACK)
+    return FALSE;
+#endif
 
-  MultiByteToWideChar(CP_ACP, 0, "Joystick", -1, lpddi->tszInstanceName, MAX_PATH);
-  /* ioctl JSIOCGNAME(len) */
-  MultiByteToWideChar(CP_ACP, 0, "Wine Joystick", -1, lpddi->tszProductName, MAX_PATH);
-  return TRUE;
+  if (!(dwFlags & DIEDFL_FORCEFEEDBACK) || joydevs[id].has_ff) {
+    fill_joystick_dideviceinstanceW(lpddi, version, id);
+    return TRUE;
+  }
+  return FALSE;
 }
 
-static JoystickImpl *alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *dinput)
+static JoystickImpl *alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *dinput, unsigned short index)
 {
-  JoystickImpl* newDevice;
-  int i;
-
-  newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
-  newDevice->lpVtbl = jvt;
-  newDevice->ref = 1;
-  newDevice->joyfd = -1;
-  newDevice->dinput = dinput;
+    JoystickImpl* newDevice;
+    LPDIDATAFORMAT df = NULL;
+    int i, idx = 0;
+    char buffer[MAX_PATH+16];
+    HKEY hkey, appkey;
+    LONG def_deadzone = 0;
+
+    newDevice = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JoystickImpl));
+    if (!newDevice) return NULL;
+
+    newDevice->base.lpVtbl = jvt;
+    newDevice->base.ref    = 1;
+    newDevice->base.guid   = *rguid;
+    newDevice->base.dinput = dinput;
+    newDevice->joyfd       = -1;
+    newDevice->joydev      = &joydevs[index];
+    list_init(&newDevice->ff_effects);
 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
-  newDevice->ff_state = FF_STATUS_STOPPED;
+    newDevice->ff_state    = FF_STATUS_STOPPED;
 #endif
-  memcpy(&(newDevice->guid),rguid,sizeof(*rguid));
-  for (i=0;i<ABS_MAX;i++) {
-    newDevice->wantmin[i] = -32768;
-    newDevice->wantmax[i] =  32767;
-    /* TODO:
-     * direct input defines a default for the deadzone somewhere; but as long
-     * as in map_axis the code for the dead zone is commented out its no
-     * problem
-     */
-    newDevice->deadz[i]   =  0;
-  }
-  fake_current_js_state(newDevice);
-  return newDevice;
-}
-
-static HRESULT joydev_create_deviceA(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
-{
-  int havejoy = 0;
-
-  havejoy = joydev_have(FALSE);
-
-  if (!havejoy)
-      return DIERR_DEVICENOTREG;
-
-  if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
-      (IsEqualGUID(&DInput_Wine_Joystick_GUID,rguid))) {
-    if ((riid == NULL) ||
-       IsEqualGUID(&IID_IDirectInputDeviceA,riid) ||
-       IsEqualGUID(&IID_IDirectInputDevice2A,riid) ||
-       IsEqualGUID(&IID_IDirectInputDevice7A,riid) ||
-       IsEqualGUID(&IID_IDirectInputDevice8A,riid)) {
-      *pdev = (IDirectInputDeviceA*) alloc_device(rguid, &JoystickAvt, dinput);
-      TRACE("Creating a Joystick device (%p)\n", *pdev);
-      return DI_OK;
-    } else
-      return DIERR_NOINTERFACE;
-  }
-
-  return DIERR_DEVICENOTREG;
-}
+    /* There is no way in linux to query force feedback autocenter status.
+       Instead, track it with ff_autocenter, and assume it's initialy
+       enabled. */
+    newDevice->ff_autocenter = 1;
+    InitializeCriticalSection(&newDevice->base.crit);
+    newDevice->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->base.crit");
+
+    /* get options */
+    get_app_key(&hkey, &appkey);
+
+    if (!get_config_key(hkey, appkey, "DefaultDeadZone", buffer, MAX_PATH))
+    {
+        def_deadzone = atoi(buffer);
+        TRACE("setting default deadzone to: %d\n", def_deadzone);
+    }
+    if (appkey) RegCloseKey(appkey);
+    if (hkey) RegCloseKey(hkey);
+
+    /* Create copy of default data format */
+    if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto failed;
+    memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
+    if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto failed;
+
+    /* Supported Axis & POVs should map 1-to-1 */
+    for (i = 0; i < WINE_JOYSTICK_MAX_AXES; i++)
+    {
+        if (!test_bit(newDevice->joydev->absbits, i)) {
+            newDevice->axes[i] = -1;
+            continue;
+        }
 
+        memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i], df->dwObjSize);
+        newDevice->axes[i] = idx;
+        newDevice->props[idx].lDevMin = newDevice->joydev->axes[i].minimum;
+        newDevice->props[idx].lDevMax = newDevice->joydev->axes[i].maximum;
+        newDevice->props[idx].lMin    = 0;
+        newDevice->props[idx].lMax    = 0xffff;
+        newDevice->props[idx].lSaturation = 0;
+        newDevice->props[idx].lDeadZone = def_deadzone;
 
-static HRESULT joydev_create_deviceW(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEW* pdev)
-{
-  int havejoy = 0;
-
-  havejoy = joydev_have(FALSE);
-
-  if (!havejoy)
-      return DIERR_DEVICENOTREG;
-
-  if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
-      (IsEqualGUID(&DInput_Wine_Joystick_GUID,rguid))) {
-    if ((riid == NULL) ||
-       IsEqualGUID(&IID_IDirectInputDeviceW,riid) ||
-       IsEqualGUID(&IID_IDirectInputDevice2W,riid) ||
-       IsEqualGUID(&IID_IDirectInputDevice7W,riid) ||
-       IsEqualGUID(&IID_IDirectInputDevice8W,riid)) {
-      *pdev = (IDirectInputDeviceW*) alloc_device(rguid, &JoystickWvt, dinput);
-      TRACE("Creating a Joystick device (%p)\n", *pdev);
-      return DI_OK;
-    } else
-      return DIERR_NOINTERFACE;
-  }
+        /* Linux supports force-feedback on X & Y axes only */
+        if (newDevice->joydev->has_ff && (i == 0 || i == 1))
+            df->rgodf[idx].dwFlags |= DIDOI_FFACTUATOR;
 
-  return DIERR_DEVICENOTREG;
-}
+        df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(newDevice->numAxes++) | DIDFT_ABSAXIS;
+    }
 
-const struct dinput_device joystick_linuxinput_device = {
-  "Wine Linux-input joystick driver",
-  joydev_enum_deviceA,
-  joydev_enum_deviceW,
-  joydev_create_deviceA,
-  joydev_create_deviceW
-};
+    for (i = 0; i < WINE_JOYSTICK_MAX_POVS; i++)
+    {
+        if (!test_bit(newDevice->joydev->absbits, ABS_HAT0X + i * 2) ||
+            !test_bit(newDevice->joydev->absbits, ABS_HAT0Y + i * 2)) {
+            newDevice->axes[ABS_HAT0X + i * 2] = newDevice->axes[ABS_HAT0Y + i * 2] = -1;
+            continue;
+        }
 
-/******************************************************************************
- *     Joystick
- */
-static ULONG WINAPI JoystickAImpl_Release(LPDIRECTINPUTDEVICE8A iface)
-{
-       JoystickImpl *This = (JoystickImpl *)iface;
-       ULONG ref;
+        memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + WINE_JOYSTICK_MAX_AXES], df->dwObjSize);
+        newDevice->axes[ABS_HAT0X + i * 2] = newDevice->axes[ABS_HAT0Y + i * 2] = i;
+        df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(newDevice->numPOVs++) | DIDFT_POV;
+    }
 
-       ref = InterlockedDecrement(&(This->ref));
-       if (ref)
-               return ref;
+    /* Buttons can be anywhere, so check all */
+    for (i = 0; i < KEY_MAX && newDevice->numButtons < WINE_JOYSTICK_MAX_BUTTONS; i++)
+    {
+        if (!test_bit(newDevice->joydev->keybits, i)) continue;
 
-       /* Reset the FF state, free all effects, etc */
-       IDirectInputDevice8_SendForceFeedbackCommand(iface, DISFFC_RESET);
+        memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[newDevice->numButtons + WINE_JOYSTICK_MAX_AXES + WINE_JOYSTICK_MAX_POVS], df->dwObjSize);
+       newDevice->buttons[i] = 0x80 | newDevice->numButtons;
+        df->rgodf[idx  ].pguid = &GUID_Button;
+        df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(newDevice->numButtons++) | DIDFT_PSHBUTTON;
+    }
+    df->dwNumObjs = idx;
 
-       /* Free the data queue */
-       HeapFree(GetProcessHeap(),0,This->data_queue);
+    fake_current_js_state(newDevice);
 
-       /* Free the DataFormat */
-       HeapFree(GetProcessHeap(), 0, This->df);
+    newDevice->base.data_format.wine_df = df;
+    IDirectInput_AddRef((LPDIRECTINPUTDEVICE8A)newDevice->base.dinput);
+    return newDevice;
 
-       HeapFree(GetProcessHeap(),0,This);
-       return 0;
+failed:
+    if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
+    HeapFree(GetProcessHeap(), 0, df);
+    HeapFree(GetProcessHeap(), 0, newDevice);
+    return NULL;
 }
 
 /******************************************************************************
-  *   SetDataFormat : the application can choose the format of the data
-  *   the device driver sends back with GetDeviceState.
+  *     get_joystick_index : Get the joystick index from a given GUID
   */
-static HRESULT WINAPI JoystickAImpl_SetDataFormat(
-       LPDIRECTINPUTDEVICE8A iface,LPCDIDATAFORMAT df
-)
+static unsigned short get_joystick_index(REFGUID guid)
 {
-  JoystickImpl *This = (JoystickImpl *)iface;
+    GUID wine_joystick = DInput_Wine_Joystick_Base_GUID;
+    GUID dev_guid = *guid;
 
-  TRACE("(this=%p,%p)\n",This,df);
+    wine_joystick.Data3 = 0;
+    dev_guid.Data3 = 0;
 
-  if (df == NULL) {
-    WARN("invalid pointer\n");
-    return E_POINTER;
-  }
+    /* for the standard joystick GUID use index 0 */
+    if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
 
-  if (df->dwSize != sizeof(*df)) {
-    WARN("invalid argument\n");
-    return DIERR_INVALIDPARAM;
-  }
+    /* for the wine joystick GUIDs use the index stored in Data3 */
+    if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3 - DInput_Wine_Joystick_Base_GUID.Data3;
 
-  _dump_DIDATAFORMAT(df);
+    return MAX_JOYDEV;
+}
 
-  if (This->joyfd!=-1) {
-    WARN("acquired\n");
-    return DIERR_ACQUIRED;
-  }
+static HRESULT joydev_create_deviceA(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
+{
+    unsigned short index;
+
+    find_joydevs();
+
+    if ((index = get_joystick_index(rguid)) < MAX_JOYDEV &&
+        have_joydevs && index < have_joydevs)
+    {
+        if ((riid == NULL) ||
+            IsEqualGUID(&IID_IDirectInputDeviceA,  riid) ||
+            IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
+            IsEqualGUID(&IID_IDirectInputDevice7A, riid) ||
+            IsEqualGUID(&IID_IDirectInputDevice8A, riid))
+        {
+            *pdev = (IDirectInputDeviceA*) alloc_device(rguid, &JoystickAvt, dinput, index);
+            TRACE("Created a Joystick device (%p)\n", *pdev);
+
+            if (*pdev == NULL)
+            {
+                ERR("out of memory\n");
+                return DIERR_OUTOFMEMORY;
+            }
+            return DI_OK;
+        }
 
-  /* Store the new data format */
-  This->df = HeapAlloc(GetProcessHeap(),0,df->dwSize);
-  if (This->df==NULL) {
-    return DIERR_OUTOFMEMORY;
-  }
-  memcpy(This->df, df, df->dwSize);
-  This->df->rgodf = HeapAlloc(GetProcessHeap(),0,df->dwNumObjs*df->dwObjSize);
-  if (This->df->rgodf==NULL) {
-    HeapFree(GetProcessHeap(), 0, This->df);
-    return DIERR_OUTOFMEMORY;
-  }
-  memcpy(This->df->rgodf,df->rgodf,df->dwNumObjs*df->dwObjSize);
+        WARN("no interface\n");
+        return DIERR_NOINTERFACE;
+    }
 
-  return DI_OK;
+    return DIERR_DEVICENOTREG;
 }
 
+
+static HRESULT joydev_create_deviceW(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEW* pdev)
+{
+    unsigned short index;
+
+    find_joydevs();
+
+    if ((index = get_joystick_index(rguid)) < MAX_JOYDEV &&
+        have_joydevs && index < have_joydevs)
+    {
+        if ((riid == NULL) ||
+            IsEqualGUID(&IID_IDirectInputDeviceW,  riid) ||
+            IsEqualGUID(&IID_IDirectInputDevice2W, riid) ||
+            IsEqualGUID(&IID_IDirectInputDevice7W, riid) ||
+            IsEqualGUID(&IID_IDirectInputDevice8W, riid))
+        {
+            *pdev = (IDirectInputDeviceW*) alloc_device(rguid, &JoystickWvt, dinput, index);
+            TRACE("Created a Joystick device (%p)\n", *pdev);
+
+            if (*pdev == NULL)
+            {
+                ERR("out of memory\n");
+                return DIERR_OUTOFMEMORY;
+            }
+            return DI_OK;
+        }
+        WARN("no interface\n");
+        return DIERR_NOINTERFACE;
+    }
+
+    WARN("invalid device GUID\n");
+    return DIERR_DEVICENOTREG;
+}
+
+const struct dinput_device joystick_linuxinput_device = {
+  "Wine Linux-input joystick driver",
+  joydev_enum_deviceA,
+  joydev_enum_deviceW,
+  joydev_create_deviceA,
+  joydev_create_deviceW
+};
+
 /******************************************************************************
   *     Acquire : gets exclusive control of the joystick
   */
 static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
 {
-    int                i;
     JoystickImpl *This = (JoystickImpl *)iface;
-    char       buf[200];
-    BOOL       readonly = TRUE;
+    HRESULT res;
 
     TRACE("(this=%p)\n",This);
-    if (This->joyfd!=-1)
-       return 0;
-    for (i=0;i<64;i++) {
-      sprintf(buf,EVDEVPREFIX"%d",i);
-      if (-1==(This->joyfd=open(buf,O_RDWR))) {
-        if (-1==(This->joyfd=open(buf,O_RDONLY))) {
-         /* Couldn't open the device at all */
-         if (errno==ENODEV)
-           return DIERR_NOTFOUND;
-         perror(buf);
-         continue;
-       }
-       else {
-         /* Couldn't open in r/w but opened in read-only. */
-          WARN("Could not open %s in read-write mode.  Force feedback will be disabled.\n",buf);
-       }
-      }
-      else {
-       /* Opened device in read-write */
-       readonly = FALSE;
-      }
-      if ((-1!=ioctl(This->joyfd,EVIOCGBIT(0,sizeof(This->evbits)),This->evbits)) &&
-         (-1!=ioctl(This->joyfd,EVIOCGBIT(EV_ABS,sizeof(This->absbits)),This->absbits)) &&
-          (-1!=ioctl(This->joyfd,EVIOCGBIT(EV_KEY,sizeof(This->keybits)),This->keybits)) &&
-          (test_bit(This->absbits,ABS_X) && test_bit(This->absbits,ABS_Y) &&
-          (test_bit(This->keybits,BTN_TRIGGER)||
-           test_bit(This->keybits,BTN_A)        ||
-           test_bit(This->keybits,BTN_1)
-         )
-        )
-      )
-       break;
-      close(This->joyfd);
-      This->joyfd = -1;
-    }
-    if (This->joyfd==-1)
-       return DIERR_NOTFOUND;
 
-    This->has_ff = FALSE;
-    This->num_effects = 0;
+    if ((res = IDirectInputDevice2AImpl_Acquire(iface)) != DI_OK)
+    {
+        WARN("Failed to acquire: %x\n", res);
+        return res;
+    }
 
-#ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
-    if (!readonly && test_bit(This->evbits, EV_FF)) {
-        if (-1!=ioctl(This->joyfd,EVIOCGBIT(EV_FF,sizeof(This->ffbits)),This->ffbits)) {
-           if (-1!=ioctl(This->joyfd,EVIOCGEFFECTS,&This->num_effects)
-               && This->num_effects > 0) {
-               This->has_ff = TRUE;
-               TRACE("Joystick seems to be capable of force feedback.\n");
-           }
-           else {
-               TRACE("Joystick does not support any effects, disabling force feedback.\n");
-           }
+    if ((This->joyfd = open(This->joydev->device, O_RDWR)) == -1)
+    {
+        if ((This->joyfd = open(This->joydev->device, O_RDONLY)) == -1)
+        {
+            /* Couldn't open the device at all */
+            ERR("Failed to open device %s: %d %s\n", This->joydev->device, errno, strerror(errno));
+            IDirectInputDevice2AImpl_Unacquire(iface);
+            return DIERR_NOTFOUND;
         }
-        else {
-            TRACE("Could not get EV_FF bits; disabling force feedback.\n");
+        else
+        {
+            /* Couldn't open in r/w but opened in read-only. */
+            WARN("Could not open %s in read-write mode.  Force feedback will be disabled.\n", This->joydev->device);
         }
     }
-    else {
-        TRACE("Force feedback disabled (device is readonly or joystick incapable).\n");
-    }
-#endif
-
-    for (i=0;i<ABS_MAX;i++) {
-       if (test_bit(This->absbits,i)) {
-         if (-1==ioctl(This->joyfd,EVIOCGABS(i),&(This->axes[i])))
-           continue;
-         FIXME("axe %d: cur=%d, min=%d, max=%d, fuzz=%d, flat=%d\n",
-             i,
-             This->axes[i][AXE_ABS],
-             This->axes[i][AXE_ABSMIN],
-             This->axes[i][AXE_ABSMAX],
-             This->axes[i][AXE_ABSFUZZ],
-             This->axes[i][AXE_ABSFLAT]
-         );
-         This->havemin[i] = This->axes[i][AXE_ABSMIN];
-         This->havemax[i] = This->axes[i][AXE_ABSMAX];
-       }
+    else
+    {
+        if (!This->ff_autocenter)
+        {
+            struct input_event event;
+
+            /* Disable autocenter. */
+            event.type = EV_FF;
+            event.code = FF_AUTOCENTER;
+            event.value = 0;
+            if (write(This->joyfd, &event, sizeof(event)) == -1)
+                ERR("Failed disabling autocenter: %d %s\n", errno, strerror(errno));
+        }
     }
-    MESSAGE("\n");
-
-       fake_current_js_state(This);
 
-    return 0;
+    return DI_OK;
 }
 
 /******************************************************************************
@@ -554,123 +692,85 @@ static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
 static HRESULT WINAPI JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
 {
     JoystickImpl *This = (JoystickImpl *)iface;
+    HRESULT res;
 
     TRACE("(this=%p)\n",This);
-    if (This->joyfd!=-1) {
-       close(This->joyfd);
-       This->joyfd = -1;
-       return DI_OK;
-    }
-    else
-       return DI_NOEFFECT;
-}
+    res = IDirectInputDevice2AImpl_Unacquire(iface);
+    if (res==DI_OK && This->joyfd!=-1) {
+      effect_list_item *itr;
+      struct input_event event;
+
+      /* For each known effect:
+       * - stop it
+       * - unload it
+       * But, unlike DISFFC_RESET, do not release the effect.
+       */
+      LIST_FOR_EACH_ENTRY(itr, &This->ff_effects, effect_list_item, entry) {
+          IDirectInputEffect_Stop(itr->ref);
+          IDirectInputEffect_Unload(itr->ref);
+      }
 
-/*
- * This maps the read value (from the input event) to a value in the
- * 'wanted' range. It also autodetects the possible range of the axe and
- * adapts values accordingly.
- */
-static int
-map_axis(JoystickImpl* This, int axis, int val) {
-    int        xmin = This->axes[axis][AXE_ABSMIN];
-    int        xmax = This->axes[axis][AXE_ABSMAX];
-    int hmax = This->havemax[axis];
-    int hmin = This->havemin[axis];
-    int        wmin = This->wantmin[axis];
-    int        wmax = This->wantmax[axis];
-    int ret;
-
-    if (val > hmax) This->havemax[axis] = hmax = val;
-    if (val < hmin) This->havemin[axis] = hmin = val;
-
-    if (xmin == xmax) return val;
-
-    /* map the value from the hmin-hmax range into the wmin-wmax range */
-    ret = ((val-hmin) * (wmax-wmin)) / (hmax-hmin) + wmin;
-
-    TRACE("xmin=%d xmax=%d hmin=%d hmax=%d wmin=%d wmax=%d val=%d ret=%d\n", xmin, xmax, hmin, hmax, wmin, wmax, val, ret);
-
-#if 0
-    /* deadzone doesn't work comfortably enough right now. needs more testing*/
-    if ((ret > -deadz/2 ) && (ret < deadz/2)) {
-        FIXME("%d in deadzone, return mid.\n",val);
-       return (wmax-wmin)/2+wmin;
+      /* Enable autocenter. */
+      event.type = EV_FF;
+      event.code = FF_AUTOCENTER;
+      /* TODO: Read autocenter strengh before disabling it, and use it here
+       * instead of 0xFFFF (maximum strengh).
+       */
+      event.value = 0xFFFF;
+      if (write(This->joyfd, &event, sizeof(event)) == -1)
+        ERR("Failed to set autocenter to %04x: %d %s\n", event.value, errno, strerror(errno));
+
+      close(This->joyfd);
+      This->joyfd = -1;
     }
-#endif
-    return ret;
+    return res;
 }
 
-/*
+/* 
  * set the current state of the js device as it would be with the middle
  * values on the axes
  */
+#define CENTER_AXIS(a) \
+    (ji->axes[a] == -1 ? 0 : joystick_map_axis( &ji->props[ji->axes[a]], \
+                                                ji->joydev->axes[a].value ))
 static void fake_current_js_state(JoystickImpl *ji)
 {
-       ji->js.lX  = map_axis(ji, ABS_X,  ji->axes[ABS_X ][AXE_ABS]);
-       ji->js.lY  = map_axis(ji, ABS_Y,  ji->axes[ABS_Y ][AXE_ABS]);
-       ji->js.lZ  = map_axis(ji, ABS_Z,  ji->axes[ABS_Z ][AXE_ABS]);
-       ji->js.lRx = map_axis(ji, ABS_RX, ji->axes[ABS_RX][AXE_ABS]);
-       ji->js.lRy = map_axis(ji, ABS_RY, ji->axes[ABS_RY][AXE_ABS]);
-       ji->js.lRz = map_axis(ji, ABS_RZ, ji->axes[ABS_RZ][AXE_ABS]);
-       ji->js.rglSlider[0] = map_axis(ji, ABS_THROTTLE, ji->axes[ABS_THROTTLE][AXE_ABS]);
-       ji->js.rglSlider[1] = map_axis(ji, ABS_RUDDER,   ji->axes[ABS_RUDDER  ][AXE_ABS]);
-}
-
-static int find_property_offset(JoystickImpl *This, LPCDIPROPHEADER ph)
-{
-  int i,c;
-  switch (ph->dwHow) {
-    case DIPH_BYOFFSET:
-      for (i=0; i<This->df->dwNumObjs; i++) {
-        if (This->df->rgodf[i].dwOfs == ph->dwObj) {
-          return i;
-        }
-      }
-      break;
-    case DIPH_BYID:
-      /* XXX: this is a hack - see below */
-      c = DIDFT_GETINSTANCE(ph->dwObj)>>WINE_JOYSTICK_AXIS_BASE;
-      for (i=0; (c&1)==0 && i<0x0F; i++) {
-        c >>= 1;
-      }
-      if (i<0x0F) {
-        return i;
-      }
-
-      /* XXX - the following part won't work with LiveForSpeed
-       * - the game sets the dwTypes to something else then
-       * the ddoi.dwType set in EnumObjects
-       */
-#if 0
-      for (i=0; i<This->df->dwNumObjs; i++) {
-        TRACE("dwType='%08x'\n", This->df->rgodf[i].dwType);
-        if ((This->df->rgodf[i].dwType & 0x00ffffff) == (ph->dwObj & 0x00ffffff)) {
-          return i;
-        }
-      }
-#endif
-      break;
-    default:
-      FIXME("Unhandled ph->dwHow=='%04X'\n", (unsigned int)ph->dwHow);
-  }
+    int i;
 
-  return -1;
+    /* center the axes */
+    ji->js.lX           = CENTER_AXIS(ABS_X);
+    ji->js.lY           = CENTER_AXIS(ABS_Y);
+    ji->js.lZ           = CENTER_AXIS(ABS_Z);
+    ji->js.lRx          = CENTER_AXIS(ABS_RX);
+    ji->js.lRy          = CENTER_AXIS(ABS_RY);
+    ji->js.lRz          = CENTER_AXIS(ABS_RZ);
+    ji->js.rglSlider[0] = CENTER_AXIS(ABS_THROTTLE);
+    ji->js.rglSlider[1] = CENTER_AXIS(ABS_RUDDER);
+
+    /* POV center is -1 */
+    for (i = 0; i < 4; i++)
+        ji->js.rgdwPOV[i] = -1;
 }
+#undef CENTER_AXIS
 
-static void joy_polldev(JoystickImpl *This) {
-    struct timeval tv;
-    fd_set     readfds;
-    struct     input_event ie;
+/* convert wine format offset to user format object index */
+static void joy_polldev(JoystickImpl *This)
+{
+    struct pollfd plfd;
+    struct input_event ie;
 
     if (This->joyfd==-1)
        return;
 
-    while (1) {
-       memset(&tv,0,sizeof(tv));
-       FD_ZERO(&readfds);
-       FD_SET(This->joyfd,&readfds);
+    while (1)
+    {
+        LONG value = 0;
+        int inst_id = -1;
+
+       plfd.fd = This->joyfd;
+       plfd.events = POLLIN;
 
-       if (1>select(This->joyfd+1,&readfds,NULL,NULL,&tv))
+       if (poll(&plfd,1,0) != 1)
            return;
 
        /* we have one event, so we can read */
@@ -680,124 +780,72 @@ static void joy_polldev(JoystickImpl *This) {
        TRACE("input_event: type %d, code %d, value %d\n",ie.type,ie.code,ie.value);
        switch (ie.type) {
        case EV_KEY:    /* button */
-           switch (ie.code) {
-           case BTN_TRIGGER:   /* normal flight stick */
-           case BTN_A:         /* gamepad */
-           case BTN_1:         /* generic */
-               This->js.rgbButtons[0] = ie.value?0x80:0x00;
-               GEN_EVENT(DIJOFS_BUTTON(0),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           case BTN_THUMB:
-           case BTN_B:
-           case BTN_2:
-               This->js.rgbButtons[1] = ie.value?0x80:0x00;
-               GEN_EVENT(DIJOFS_BUTTON(1),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           case BTN_THUMB2:
-           case BTN_C:
-           case BTN_3:
-               This->js.rgbButtons[2] = ie.value?0x80:0x00;
-               GEN_EVENT(DIJOFS_BUTTON(2),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           case BTN_TOP:
-           case BTN_X:
-           case BTN_4:
-               This->js.rgbButtons[3] = ie.value?0x80:0x00;
-               GEN_EVENT(DIJOFS_BUTTON(3),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           case BTN_TOP2:
-           case BTN_Y:
-           case BTN_5:
-               This->js.rgbButtons[4] = ie.value?0x80:0x00;
-               GEN_EVENT(DIJOFS_BUTTON(4),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           case BTN_PINKIE:
-           case BTN_Z:
-           case BTN_6:
-               This->js.rgbButtons[5] = ie.value?0x80:0x00;
-               GEN_EVENT(DIJOFS_BUTTON(5),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           case BTN_BASE:
-           case BTN_TL:
-           case BTN_7:
-               This->js.rgbButtons[6] = ie.value?0x80:0x00;
-               GEN_EVENT(DIJOFS_BUTTON(6),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           case BTN_BASE2:
-           case BTN_TR:
-           case BTN_8:
-               This->js.rgbButtons[7] = ie.value?0x80:0x00;
-               GEN_EVENT(DIJOFS_BUTTON(7),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           case BTN_BASE3:
-           case BTN_TL2:
-           case BTN_9:
-               This->js.rgbButtons[8] = ie.value?0x80:0x00;
-               GEN_EVENT(DIJOFS_BUTTON(8),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           case BTN_BASE4:
-           case BTN_TR2:
-               This->js.rgbButtons[9] = ie.value?0x80:0x00;
-               GEN_EVENT(DIJOFS_BUTTON(9),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           case BTN_BASE5:
-           case BTN_SELECT:
-               This->js.rgbButtons[10] = ie.value?0x80:0x00;
-               GEN_EVENT(DIJOFS_BUTTON(10),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           default:
-               FIXME("unhandled joystick button %x, value %d\n",ie.code,ie.value);
-               break;
-           }
-           break;
+        {
+            int btn = This->buttons[ie.code];
+
+            TRACE("(%p) %d -> %d\n", This, ie.code, btn);
+            if (btn & 0x80)
+            {
+                btn &= 0x7F;
+                inst_id = DIDFT_MAKEINSTANCE(btn) | DIDFT_PSHBUTTON;
+                This->js.rgbButtons[btn] = value = ie.value ? 0x80 : 0x00;
+            }
+            break;
+        }
        case EV_ABS:
-           switch (ie.code) {
-           case ABS_X:
-               This->js.lX = map_axis(This,ABS_X,ie.value);
-               GEN_EVENT(DIJOFS_X,This->js.lX,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           case ABS_Y:
-               This->js.lY = map_axis(This,ABS_Y,ie.value);
-               GEN_EVENT(DIJOFS_Y,This->js.lY,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           case ABS_Z:
-               This->js.lZ = map_axis(This,ABS_Z,ie.value);
-               GEN_EVENT(DIJOFS_Z,This->js.lZ,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           case ABS_RX:
-               This->js.lRx = map_axis(This,ABS_RX,ie.value);
-               GEN_EVENT(DIJOFS_RX,This->js.lRx,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           case ABS_RY:
-               This->js.lRy = map_axis(This,ABS_RY,ie.value);
-               GEN_EVENT(DIJOFS_RY,This->js.lRy,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           case ABS_RZ:
-               This->js.lRz = map_axis(This,ABS_RZ,ie.value);
-               GEN_EVENT(DIJOFS_RZ,This->js.lRz,ie.time.tv_usec,(This->dinput->evsequence)++);
-               break;
-           case ABS_THROTTLE:
-                This->js.rglSlider[0] = map_axis(This,ABS_THROTTLE,ie.value);
-                GEN_EVENT(DIJOFS_SLIDER(0),This->js.rglSlider[0],ie.time.tv_usec,(This->dinput->evsequence)++);
+        {
+            int axis = This->axes[ie.code];
+            if (axis==-1) {
                 break;
-           case ABS_RUDDER:
-                This->js.rglSlider[1] = map_axis(This,ABS_RUDDER,ie.value);
-                GEN_EVENT(DIJOFS_SLIDER(1),This->js.rglSlider[1],ie.time.tv_usec,(This->dinput->evsequence)++);
+            }
+            inst_id = DIDFT_MAKEINSTANCE(axis) | (ie.code < ABS_HAT0X ? DIDFT_ABSAXIS : DIDFT_POV);
+            value = joystick_map_axis(&This->props[id_to_object(This->base.data_format.wine_df, inst_id)], ie.value);
+
+           switch (ie.code) {
+            case ABS_X:         This->js.lX  = value; break;
+            case ABS_Y:         This->js.lY  = value; break;
+            case ABS_Z:         This->js.lZ  = value; break;
+            case ABS_RX:        This->js.lRx = value; break;
+            case ABS_RY:        This->js.lRy = value; break;
+            case ABS_RZ:        This->js.lRz = value; break;
+            case ABS_THROTTLE:  This->js.rglSlider[0] = value; break;
+            case ABS_RUDDER:    This->js.rglSlider[1] = value; break;
+            case ABS_HAT0X: case ABS_HAT0Y: case ABS_HAT1X: case ABS_HAT1Y:
+            case ABS_HAT2X: case ABS_HAT2Y: case ABS_HAT3X: case ABS_HAT3Y:
+            {
+                int idx = (ie.code - ABS_HAT0X) / 2;
+
+                if (ie.code % 2)
+                    This->povs[idx].y = ie.value;
+                else
+                    This->povs[idx].x = ie.value;
+
+                This->js.rgdwPOV[idx] = value = joystick_map_pov(&This->povs[idx]);
                 break;
+            }
            default:
-               FIXME("unhandled joystick axe event (code %d, value %d)\n",ie.code,ie.value);
-               break;
+               FIXME("unhandled joystick axis event (code %d, value %d)\n",ie.code,ie.value);
            }
            break;
+        }
 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
        case EV_FF_STATUS:
            This->ff_state = ie.value;
            break;
+#endif
+#ifdef EV_SYN
+       case EV_SYN:
+           /* there is nothing to do */
+           break;
 #endif
        default:
            FIXME("joystick cannot handle type %d event (code %d)\n",ie.type,ie.code);
            break;
        }
+        if (inst_id >= 0)
+            queue_event((LPDIRECTINPUTDEVICE8A)This,
+                        id_to_offset(&This->base.data_format, inst_id),
+                        value, ie.time.tv_usec, This->base.dinput->evsequence++);
     }
 }
 
@@ -810,91 +858,20 @@ static HRESULT WINAPI JoystickAImpl_GetDeviceState(
 ) {
     JoystickImpl *This = (JoystickImpl *)iface;
 
-    joy_polldev(This);
+    TRACE("(this=%p,0x%08x,%p)\n", This, len, ptr);
 
-    TRACE("(this=%p,0x%08lx,%p)\n",This,len,ptr);
-    if ((len != sizeof(DIJOYSTATE)) && (len != sizeof(DIJOYSTATE2))) {
-       FIXME("len %ld is not sizeof(DIJOYSTATE) or DIJOYSTATE2, unsupported format.\n",len);
-       return E_FAIL;
+    if (!This->base.acquired)
+    {
+        WARN("not acquired\n");
+        return DIERR_NOTACQUIRED;
     }
-    memcpy(ptr,&(This->js),len);
-    This->queue_head = 0;
-    This->queue_tail = 0;
-    return 0;
-}
 
-/******************************************************************************
-  *     GetDeviceData : gets buffered input data.
-  */
-static HRESULT WINAPI JoystickAImpl_GetDeviceData(LPDIRECTINPUTDEVICE8A iface,
-                                             DWORD dodsize,
-                                             LPDIDEVICEOBJECTDATA dod,
-                                             LPDWORD entries,
-                                             DWORD flags
-) {
-  JoystickImpl *This = (JoystickImpl *)iface;
-  DWORD len;
-  int nqtail;
-  HRESULT hr = DI_OK;
-
-  TRACE("(%p)->(dods=%ld,entries=%ld,fl=0x%08lx)\n",This,dodsize,*entries,flags);
-
-  if (This->joyfd==-!1) {
-    WARN("not acquired\n");
-    return DIERR_NOTACQUIRED;
-  }
-
-  joy_polldev(This);
-  if (flags & DIGDD_PEEK)
-    FIXME("DIGDD_PEEK\n");
-
-  len = ((This->queue_head < This->queue_tail) ? This->queue_len : 0)
-    + (This->queue_head - This->queue_tail);
-  if (len > *entries)
-    len = *entries;
-
-  if (dod == NULL) {
-    if (len)
-      TRACE("Application discarding %ld event(s).\n", len);
-
-    *entries = len;
-    nqtail = This->queue_tail + len;
-    while (nqtail >= This->queue_len)
-      nqtail -= This->queue_len;
-  } else {
-    if (dodsize < sizeof(DIDEVICEOBJECTDATA_DX3)) {
-      ERR("Wrong structure size !\n");
-      return DIERR_INVALIDPARAM;
-  }
-
-    if (len)
-      TRACE("Application retrieving %ld event(s).\n", len);
-
-    *entries = 0;
-    nqtail = This->queue_tail;
-    while (len) {
-      /* Copy the buffered data into the application queue */
-      memcpy((char *)dod + *entries * dodsize, This->data_queue + nqtail, dodsize);
-      /* Advance position */
-      nqtail++;
-      if (nqtail >= This->queue_len)
-        nqtail -= This->queue_len;
-      (*entries)++;
-      len--;
-    }
-  }
-
-  if (This->overflow) {
-    hr = DI_BUFFEROVERFLOW;
-    if (!(flags & DIGDD_PEEK)) {
-      This->overflow = FALSE;
-    }
-  }
+    joy_polldev(This);
 
-  if (!(flags & DIGDD_PEEK))
-    This->queue_tail = nqtail;
+    /* convert and copy data to user supplied buffer */
+    fill_DataFormat(ptr, len, &This->js, &This->base.data_format);
 
-  return hr;
+    return DI_OK;
 }
 
 /******************************************************************************
@@ -912,81 +889,96 @@ static HRESULT WINAPI JoystickAImpl_SetProperty(LPDIRECTINPUTDEVICE8A iface,
   }
 
   TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
-  TRACE("ph.dwSize = %ld, ph.dwHeaderSize =%ld, ph.dwObj = %ld, ph.dwHow= %ld\n",ph->dwSize, ph->dwHeaderSize,ph->dwObj,ph->dwHow);
+  TRACE("ph.dwSize = %d, ph.dwHeaderSize =%d, ph.dwObj = %d, ph.dwHow= %d\n",
+        ph->dwSize, ph->dwHeaderSize, ph->dwObj, ph->dwHow);
 
   if (!HIWORD(rguid)) {
     switch (LOWORD(rguid)) {
-    case (DWORD) DIPROP_BUFFERSIZE: {
-      LPCDIPROPDWORD   pd = (LPCDIPROPDWORD)ph;
+    case (DWORD_PTR)DIPROP_RANGE: {
+      LPCDIPROPRANGE pr = (LPCDIPROPRANGE)ph;
 
-      TRACE("buffersize = %ld\n",pd->dwData);
-      if (This->data_queue) {
-        This->data_queue = HeapReAlloc(GetProcessHeap(),0, This->data_queue, pd->dwData * sizeof(DIDEVICEOBJECTDATA));
+      if (ph->dwHow == DIPH_DEVICE) {
+        DWORD i;
+        TRACE("proprange(%d,%d) all\n", pr->lMin, pr->lMax);
+        for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++) {
+          /* Scale dead-zone */
+          This->props[i].lDeadZone = MulDiv(This->props[i].lDeadZone, pr->lMax - pr->lMin,
+                                            This->props[i].lMax - This->props[i].lMin);
+          This->props[i].lMin = pr->lMin;
+          This->props[i].lMax = pr->lMax;
+        }
       } else {
-        This->data_queue = HeapAlloc(GetProcessHeap(),0, pd->dwData * sizeof(DIDEVICEOBJECTDATA));
+        int obj = find_property(&This->base.data_format, ph);
+
+        TRACE("proprange(%d,%d) obj=%d\n", pr->lMin, pr->lMax, obj);
+        if (obj >= 0) {
+          /* Scale dead-zone */
+          This->props[obj].lDeadZone = MulDiv(This->props[obj].lDeadZone, pr->lMax - pr->lMin,
+                                              This->props[obj].lMax - This->props[obj].lMin);
+          This->props[obj].lMin = pr->lMin;
+          This->props[obj].lMax = pr->lMax;
+        }
       }
-      This->queue_head = 0;
-      This->queue_tail = 0;
-      This->queue_len  = pd->dwData;
+      fake_current_js_state(This);
       break;
     }
-    case (DWORD)DIPROP_RANGE: {
-      LPCDIPROPRANGE   pr = (LPCDIPROPRANGE)ph;
-
+    case (DWORD_PTR)DIPROP_DEADZONE: {
+      LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
       if (ph->dwHow == DIPH_DEVICE) {
-        int i;
-        TRACE("proprange(%ld,%ld) all\n",pr->lMin,pr->lMax);
-        for (i = 0; i < This->df->dwNumObjs; i++) {
-          This->wantmin[i] = pr->lMin;
-          This->wantmax[i] = pr->lMax;
+        DWORD i;
+        TRACE("deadzone(%d) all\n", pd->dwData);
+        for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++) {
+          This->props[i].lDeadZone = pd->dwData;
         }
       } else {
-        int obj = find_property_offset(This, ph);
-        TRACE("proprange(%ld,%ld) obj=%d\n",pr->lMin,pr->lMax,obj);
+        int obj = find_property(&This->base.data_format, ph);
+
+        TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
         if (obj >= 0) {
-          This->wantmin[obj] = pr->lMin;
-          This->wantmax[obj] = pr->lMax;
+          This->props[obj].lDeadZone = pd->dwData;
         }
       }
-      return DI_OK;
+      fake_current_js_state(This);
+      break;
     }
-    case (DWORD)DIPROP_DEADZONE: {
+    case (DWORD_PTR)DIPROP_CALIBRATIONMODE: {
       LPCDIPROPDWORD   pd = (LPCDIPROPDWORD)ph;
+      FIXME("DIPROP_CALIBRATIONMODE(%d)\n", pd->dwData);
+      break;
+    }
+    case (DWORD_PTR)DIPROP_AUTOCENTER: {
+      LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
+
+      TRACE("autocenter(%d)\n", pd->dwData);
+      This->ff_autocenter = pd->dwData == DIPROPAUTOCENTER_ON;
+
+      break;
+    }
+    case (DWORD_PTR)DIPROP_SATURATION: {
+      LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
+
       if (ph->dwHow == DIPH_DEVICE) {
-        int i;
-        TRACE("deadzone(%ld) all\n",pd->dwData);
-        for (i = 0; i < This->df->dwNumObjs; i++) {
-          This->deadz[i] = pd->dwData;
-        }
+        DWORD i;
+
+        TRACE("saturation(%d) all\n", pd->dwData);
+        for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++)
+          This->props[i].lSaturation = pd->dwData;
       } else {
-        int obj = find_property_offset(This, ph);
-        TRACE("deadzone(%ld) obj=%d\n",pd->dwData,obj);
-        if (obj >= 0) {
-          This->deadz[obj] = pd->dwData;
-        }
+          int obj = find_property(&This->base.data_format, ph);
+
+          if (obj < 0) return DIERR_OBJECTNOTFOUND;
+
+          TRACE("saturation(%d) obj=%d\n", pd->dwData, obj);
+          This->props[obj].lSaturation = pd->dwData;
       }
-      return DI_OK;
+      fake_current_js_state(This);
+      break;
     }
     default:
-      FIXME("Unknown type %p (%s)\n",rguid,debugstr_guid(rguid));
-      break;
+      return IDirectInputDevice2AImpl_SetProperty(iface, rguid, ph);
     }
   }
-  fake_current_js_state(This);
-  return 0;
-}
-
-/******************************************************************************
-  *     SetEventNotification : specifies event to be sent on state change
-  */
-static HRESULT WINAPI JoystickAImpl_SetEventNotification(
-       LPDIRECTINPUTDEVICE8A iface, HANDLE hnd
-) {
-    JoystickImpl *This = (JoystickImpl *)iface;
-
-    TRACE("(this=%p,%p)\n",This,hnd);
-    This->hEvent = hnd;
-    return DI_OK;
+  return DI_OK;
 }
 
 static HRESULT WINAPI JoystickAImpl_GetCapabilities(
@@ -994,8 +986,6 @@ static HRESULT WINAPI JoystickAImpl_GetCapabilities(
        LPDIDEVCAPS lpDIDevCaps)
 {
     JoystickImpl *This = (JoystickImpl *)iface;
-    int                xfd = This->joyfd;
-    int                i,axes,buttons;
 
     TRACE("%p->(%p)\n",iface,lpDIDevCaps);
 
@@ -1009,293 +999,147 @@ static HRESULT WINAPI JoystickAImpl_GetCapabilities(
         return DIERR_INVALIDPARAM;
     }
 
-    if (xfd==-1) {
-       /* yes, games assume we return something, even if unacquired */
-       JoystickAImpl_Acquire(iface);
-    }
-
     lpDIDevCaps->dwFlags       = DIDC_ATTACHED;
-    if (This->dinput->dwVersion >= 0x0800)
+    if (This->base.dinput->dwVersion >= 0x0800)
         lpDIDevCaps->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
     else
         lpDIDevCaps->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
 
-    axes=0;
-    for (i=0;i<ABS_MAX;i++) if (test_bit(This->absbits,i)) axes++;
-    buttons=0;
-    for (i=0;i<KEY_MAX;i++) if (test_bit(This->keybits,i)) buttons++;
-
-    if (This->has_ff)
+    if (This->joydev->has_ff) 
         lpDIDevCaps->dwFlags |= DIDC_FORCEFEEDBACK;
 
-    lpDIDevCaps->dwAxes = axes;
-    lpDIDevCaps->dwButtons = buttons;
-
-    if (xfd==-1) {
-      JoystickAImpl_Unacquire(iface);
-    }
+    lpDIDevCaps->dwAxes = This->numAxes;
+    lpDIDevCaps->dwButtons = This->numButtons;
+    lpDIDevCaps->dwPOVs = This->numPOVs;
 
     return DI_OK;
 }
 
-static HRESULT WINAPI JoystickAImpl_Poll(LPDIRECTINPUTDEVICE8A iface) {
+static HRESULT WINAPI JoystickAImpl_Poll(LPDIRECTINPUTDEVICE8A iface)
+{
     JoystickImpl *This = (JoystickImpl *)iface;
+
     TRACE("(%p)\n",This);
 
-    if (This->joyfd==-1) {
-      return DIERR_NOTACQUIRED;
-    }
+    if (!This->base.acquired)
+        return DIERR_NOTACQUIRED;
 
     joy_polldev(This);
     return DI_OK;
 }
 
 /******************************************************************************
-  *     EnumObjects : enumerate the different buttons and axis...
+  *     GetProperty : get input device properties
   */
-static HRESULT WINAPI JoystickAImpl_EnumObjects(
-       LPDIRECTINPUTDEVICE8A iface,
-       LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback,
-       LPVOID lpvRef,
-       DWORD dwFlags)
+static HRESULT WINAPI JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface,
+                                               REFGUID rguid,
+                                               LPDIPROPHEADER pdiph)
 {
-  JoystickImpl *This = (JoystickImpl *)iface;
-  DIDEVICEOBJECTINSTANCEA ddoi;
-  int xfd = This->joyfd;
-
-  TRACE("(this=%p,%p,%p,%08lx)\n", This, lpCallback, lpvRef, dwFlags);
-  if (TRACE_ON(dinput)) {
-    TRACE("  - flags = ");
-    _dump_EnumObjects_flags(dwFlags);
-    TRACE("\n");
-  }
+    JoystickImpl *This = (JoystickImpl *)iface;
 
-  /* We need to work even if we're not yet acquired */
-  if (xfd == -1)
-    IDirectInputDevice8_Acquire(iface);
-
-  /* Only the fields till dwFFMaxForce are relevant */
-  ddoi.dwSize = FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA, dwFFMaxForce);
-
-  /* For the joystick, do as is done in the GetCapabilities function */
-  /* FIXME: needs more items */
-  if ((dwFlags == DIDFT_ALL) ||
-      (dwFlags & DIDFT_AXIS)) {
-    BYTE i;
-
-    for (i = 0; i < ABS_MAX; i++) {
-      if (!test_bit(This->absbits,i)) continue;
-
-      switch (i) {
-      case ABS_X:
-       ddoi.guidType = GUID_XAxis;
-       ddoi.dwOfs = DIJOFS_X;
-       break;
-      case ABS_Y:
-       ddoi.guidType = GUID_YAxis;
-       ddoi.dwOfs = DIJOFS_Y;
-       break;
-      case ABS_Z:
-       ddoi.guidType = GUID_ZAxis;
-       ddoi.dwOfs = DIJOFS_Z;
-       break;
-      case ABS_RX:
-       ddoi.guidType = GUID_RxAxis;
-       ddoi.dwOfs = DIJOFS_RX;
-       break;
-      case ABS_RY:
-       ddoi.guidType = GUID_RyAxis;
-       ddoi.dwOfs = DIJOFS_RY;
-       break;
-      case ABS_RZ:
-       ddoi.guidType = GUID_RzAxis;
-       ddoi.dwOfs = DIJOFS_RZ;
-       break;
-      case ABS_THROTTLE:
-       ddoi.guidType = GUID_Slider;
-       ddoi.dwOfs = DIJOFS_SLIDER(0);
-       break;
-      case ABS_RUDDER:
-       ddoi.guidType = GUID_Slider;
-       ddoi.dwOfs = DIJOFS_SLIDER(1);
-       break;
-      default:
-       FIXME("unhandled abs axis %d, ignoring!\n",i);
-      }
-      ddoi.dwType = DIDFT_MAKEINSTANCE((1<<i) << WINE_JOYSTICK_AXIS_BASE) | DIDFT_ABSAXIS;
-      /* Linux event force feedback supports only (and always) x and y axes */
-      if (i == ABS_X || i == ABS_Y) {
-       if (This->has_ff)
-         ddoi.dwFlags |= DIDOI_FFACTUATOR;
-      }
-      sprintf(ddoi.tszName, "%d-Axis", i);
-      _dump_OBJECTINSTANCEA(&ddoi);
-      if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) {
-       /* return to unaquired state if that's where we were */
-       if (xfd == -1)
-         IDirectInputDevice8_Unacquire(iface);
-       return DI_OK;
-      }
-    }
-  }
+    TRACE("(this=%p,%s,%p)\n", iface, debugstr_guid(rguid), pdiph);
+    _dump_DIPROPHEADER(pdiph);
 
-  if ((dwFlags == DIDFT_ALL) ||
-      (dwFlags & DIDFT_BUTTON)) {
-    int i;
+    if (HIWORD(rguid)) return DI_OK;
 
-    /*The DInput SDK says that GUID_Button is only for mouse buttons but well*/
-
-    ddoi.guidType = GUID_Button;
-
-    for (i = 0; i < KEY_MAX; i++) {
-      if (!test_bit(This->keybits,i)) continue;
-
-      switch (i) {
-      case BTN_TRIGGER:
-      case BTN_A:
-      case BTN_1:
-         ddoi.dwOfs = DIJOFS_BUTTON(0);
-         ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 0) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
-         break;
-       case BTN_THUMB:
-       case BTN_B:
-       case BTN_2:
-         ddoi.dwOfs = DIJOFS_BUTTON(1);
-         ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 1) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
-         break;
-       case BTN_THUMB2:
-       case BTN_C:
-       case BTN_3:
-         ddoi.dwOfs = DIJOFS_BUTTON(2);
-         ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 2) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
-         break;
-       case BTN_TOP:
-       case BTN_X:
-       case BTN_4:
-         ddoi.dwOfs = DIJOFS_BUTTON(3);
-         ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 3) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
-         break;
-       case BTN_TOP2:
-       case BTN_Y:
-       case BTN_5:
-         ddoi.dwOfs = DIJOFS_BUTTON(4);
-         ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 4) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
-         break;
-       case BTN_PINKIE:
-       case BTN_Z:
-       case BTN_6:
-         ddoi.dwOfs = DIJOFS_BUTTON(5);
-         ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 5) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
-         break;
-       case BTN_BASE:
-       case BTN_TL:
-       case BTN_7:
-         ddoi.dwOfs = DIJOFS_BUTTON(6);
-         ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 6) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
-         break;
-       case BTN_BASE2:
-       case BTN_TR:
-       case BTN_8:
-         ddoi.dwOfs = DIJOFS_BUTTON(7);
-         ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 7) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
-         break;
-       case BTN_BASE3:
-       case BTN_TL2:
-       case BTN_9:
-         ddoi.dwOfs = DIJOFS_BUTTON(8);
-         ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 8) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
-         break;
-       case BTN_BASE4:
-       case BTN_TR2:
-         ddoi.dwOfs = DIJOFS_BUTTON(9);
-         ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 9) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
-         break;
-       case BTN_BASE5:
-       case BTN_SELECT:
-         ddoi.dwOfs = DIJOFS_BUTTON(10);
-         ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 10) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
-         break;
-      }
-      sprintf(ddoi.tszName, "%d-Button", i);
-      _dump_OBJECTINSTANCEA(&ddoi);
-      if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) {
-       /* return to unaquired state if that's where we were */
-       if (xfd == -1)
-         IDirectInputDevice8_Unacquire(iface);
-       return DI_OK;
-      }
+    switch (LOWORD(rguid)) {
+    case (DWORD_PTR) DIPROP_RANGE:
+    {
+        LPDIPROPRANGE pr = (LPDIPROPRANGE) pdiph;
+        int obj = find_property(&This->base.data_format, pdiph);
+
+        if (obj < 0) return DIERR_OBJECTNOTFOUND;
+
+        pr->lMin = This->props[obj].lMin;
+        pr->lMax = This->props[obj].lMax;
+       TRACE("range(%d, %d) obj=%d\n", pr->lMin, pr->lMax, obj);
+        break;
     }
-  }
+    case (DWORD_PTR) DIPROP_DEADZONE:
+    {
+        LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
+        int obj = find_property(&This->base.data_format, pdiph);
 
-  /* return to unaquired state if that's where we were */
-  if (xfd == -1)
-    IDirectInputDevice8_Unacquire(iface);
+        if (obj < 0) return DIERR_OBJECTNOTFOUND;
 
-  return DI_OK;
-}
+        pd->dwData = This->props[obj].lDeadZone;
+        TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
+        break;
+    }
+    case (DWORD_PTR) DIPROP_SATURATION:
+    {
+        LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
+        int obj = find_property(&This->base.data_format, pdiph);
 
-static HRESULT WINAPI JoystickWImpl_EnumObjects(LPDIRECTINPUTDEVICE8W iface,
-                                               LPDIENUMDEVICEOBJECTSCALLBACKW lpCallback,
-                                               LPVOID lpvRef,
-                                               DWORD dwFlags)
-{
-  JoystickImpl *This = (JoystickImpl *)iface;
+        if (obj < 0) return DIERR_OBJECTNOTFOUND;
 
-  device_enumobjects_AtoWcb_data data;
+        pd->dwData = This->props[obj].lSaturation;
+        TRACE("saturation(%d) obj=%d\n", pd->dwData, obj);
+        break;
+    }
+    case (DWORD_PTR) DIPROP_AUTOCENTER:
+    {
+        LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
+
+        pd->dwData = This->ff_autocenter ? DIPROPAUTOCENTER_ON : DIPROPAUTOCENTER_OFF;
+        TRACE("autocenter(%d)\n", pd->dwData);
+        break;
+    }
 
-  data.lpCallBack = lpCallback;
-  data.lpvRef = lpvRef;
+    default:
+        return IDirectInputDevice2AImpl_GetProperty(iface, rguid, pdiph);
+    }
 
-  return JoystickAImpl_EnumObjects((LPDIRECTINPUTDEVICE8A) This, (LPDIENUMDEVICEOBJECTSCALLBACKA) DIEnumDevicesCallbackAtoW, (LPVOID) &data, dwFlags);
+    return DI_OK;
 }
 
 /******************************************************************************
-  *     GetProperty : get input device properties
+  *     GetObjectInfo : get information about a device object such as a button
+  *                     or axis
   */
-static HRESULT WINAPI JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface,
-                                               REFGUID rguid,
-                                               LPDIPROPHEADER pdiph)
+static HRESULT WINAPI JoystickWImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8W iface,
+        LPDIDEVICEOBJECTINSTANCEW pdidoi, DWORD dwObj, DWORD dwHow)
 {
-  JoystickImpl *This = (JoystickImpl *)iface;
-
-  TRACE("(this=%p,%s,%p)\n",
-       iface, debugstr_guid(rguid), pdiph);
-
-  if (TRACE_ON(dinput))
-    _dump_DIPROPHEADER(pdiph);
-
-  if (!HIWORD(rguid)) {
-    switch (LOWORD(rguid)) {
-    case (DWORD) DIPROP_BUFFERSIZE: {
-      LPDIPROPDWORD    pd = (LPDIPROPDWORD)pdiph;
-
-      TRACE(" return buffersize = %d\n",This->queue_len);
-      pd->dwData = This->queue_len;
-      break;
-    }
-
-    case (DWORD) DIPROP_RANGE: {
-      /* LPDIPROPRANGE pr = (LPDIPROPRANGE) pdiph; */
-      if ((pdiph->dwHow == DIPH_BYID) &&
-         (pdiph->dwObj & DIDFT_ABSAXIS)) {
-       /* The app is querying the current range of the axis : return the lMin and lMax values */
-       FIXME("unimplemented axis range query.\n");
-      }
+    static const WCHAR axisW[] = {'A','x','i','s',' ','%','d',0};
+    static const WCHAR povW[] = {'P','O','V',' ','%','d',0};
+    static const WCHAR buttonW[] = {'B','u','t','t','o','n',' ','%','d',0};
+    HRESULT res;
+
+    res = IDirectInputDevice2WImpl_GetObjectInfo(iface, pdidoi, dwObj, dwHow);
+    if (res != DI_OK) return res;
+
+    if      (pdidoi->dwType & DIDFT_AXIS)
+        sprintfW(pdidoi->tszName, axisW, DIDFT_GETINSTANCE(pdidoi->dwType));
+    else if (pdidoi->dwType & DIDFT_POV)
+        sprintfW(pdidoi->tszName, povW, DIDFT_GETINSTANCE(pdidoi->dwType));
+    else if (pdidoi->dwType & DIDFT_BUTTON)
+        sprintfW(pdidoi->tszName, buttonW, DIDFT_GETINSTANCE(pdidoi->dwType));
+
+    _dump_OBJECTINSTANCEW(pdidoi);
+    return res;
+}
 
-      break;
-    }
+static HRESULT WINAPI JoystickAImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8A iface,
+        LPDIDEVICEOBJECTINSTANCEA pdidoi, DWORD dwObj, DWORD dwHow)
+{
+    HRESULT res;
+    DIDEVICEOBJECTINSTANCEW didoiW;
+    DWORD dwSize = pdidoi->dwSize;
 
-    default:
-      FIXME("Unknown type %p (%s)\n",rguid,debugstr_guid(rguid));
-      break;
-    }
-  }
+    didoiW.dwSize = sizeof(didoiW);
+    res = JoystickWImpl_GetObjectInfo((LPDIRECTINPUTDEVICE8W)iface, &didoiW, dwObj, dwHow);
+    if (res != DI_OK) return res;
 
+    memset(pdidoi, 0, pdidoi->dwSize);
+    memcpy(pdidoi, &didoiW, FIELD_OFFSET(DIDEVICEOBJECTINSTANCEW, tszName));
+    pdidoi->dwSize = dwSize;
+    WideCharToMultiByte(CP_ACP, 0, didoiW.tszName, -1, pdidoi->tszName,
+                        sizeof(pdidoi->tszName), NULL, NULL);
 
-  return DI_OK;
+    return res;
 }
 
-/******************************************************************************
+/****************************************************************************** 
   *    CreateEffect - Create a new FF effect with the specified params
   */
 static HRESULT WINAPI JoystickAImpl_CreateEffect(LPDIRECTINPUTDEVICE8A iface,
@@ -1305,7 +1149,7 @@ static HRESULT WINAPI JoystickAImpl_CreateEffect(LPDIRECTINPUTDEVICE8A iface,
                                                 LPUNKNOWN pUnkOuter)
 {
 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
-    EffectListItem* new = NULL;
+    effect_list_item* new_effect = NULL;
     HRESULT retval = DI_OK;
 #endif
 
@@ -1318,20 +1162,29 @@ static HRESULT WINAPI JoystickAImpl_CreateEffect(LPDIRECTINPUTDEVICE8A iface,
     return DI_OK;
 #else
 
-    new = malloc(sizeof(EffectListItem));
-    new->next = This->top_effect;
-    This->top_effect = new;
+    if (!(new_effect = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_effect))))
+        return DIERR_OUTOFMEMORY;
 
-    retval = linuxinput_create_effect(&(This->joyfd), rguid, &(new->ref));
+    retval = linuxinput_create_effect(&This->joyfd, rguid, &new_effect->entry, &new_effect->ref);
     if (retval != DI_OK)
-       return retval;
+    {
+        HeapFree(GetProcessHeap(), 0, new_effect);
+        return retval;
+    }
 
     if (lpeff != NULL)
-       retval = IDirectInputEffect_SetParameters(new->ref, lpeff, 0);
-    if (retval != DI_OK && retval != DI_DOWNLOADSKIPPED)
-       return retval;
+    {
+        retval = IDirectInputEffect_SetParameters(new_effect->ref, lpeff, 0);
 
-    *ppdef = new->ref;
+        if (retval != DI_OK && retval != DI_DOWNLOADSKIPPED)
+        {
+            HeapFree(GetProcessHeap(), 0, new_effect);
+            return retval;
+        }
+    }
+
+    list_add_tail(&This->ff_effects, &new_effect->entry);
+    *ppdef = new_effect->ref;
 
     if (pUnkOuter != NULL)
        FIXME("Interface aggregation not implemented.\n");
@@ -1339,7 +1192,7 @@ static HRESULT WINAPI JoystickAImpl_CreateEffect(LPDIRECTINPUTDEVICE8A iface,
     return DI_OK;
 
 #endif /* HAVE_STRUCT_FF_EFFECT_DIRECTION */
-}
+} 
 
 /*******************************************************************************
  *     EnumEffects - Enumerate available FF effects
@@ -1353,74 +1206,66 @@ static HRESULT WINAPI JoystickAImpl_EnumEffects(LPDIRECTINPUTDEVICE8A iface,
     DIEFFECTINFOA dei; /* feif */
     DWORD type = DIEFT_GETTYPE(dwEffType);
     JoystickImpl* This = (JoystickImpl*)iface;
-    int xfd = This->joyfd;
 
-    TRACE("(this=%p,%p,%ld) type=%ld fd=%d\n", This, pvRef, dwEffType, type, xfd);
+    TRACE("(this=%p,%p,%d) type=%d\n", This, pvRef, dwEffType, type);
 
-    dei.dwSize = sizeof(DIEFFECTINFOA);
-
-    /* We need to return something even if we're not yet acquired */
-    if (xfd == -1)
-       IDirectInputDevice8_Acquire(iface);
+    dei.dwSize = sizeof(DIEFFECTINFOA);          
 
     if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
-       && test_bit(This->ffbits, FF_CONSTANT)) {
+       && test_bit(This->joydev->ffbits, FF_CONSTANT)) {
        IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce);
        (*lpCallback)(&dei, pvRef);
     }
 
     if ((type == DIEFT_ALL || type == DIEFT_PERIODIC)
-       && test_bit(This->ffbits, FF_PERIODIC)) {
-       if (test_bit(This->ffbits, FF_SQUARE)) {
+       && test_bit(This->joydev->ffbits, FF_PERIODIC)) {
+       if (test_bit(This->joydev->ffbits, FF_SQUARE)) {
            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Square);
            (*lpCallback)(&dei, pvRef);
        }
-       if (test_bit(This->ffbits, FF_SINE)) {
+       if (test_bit(This->joydev->ffbits, FF_SINE)) {
             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine);
            (*lpCallback)(&dei, pvRef);
        }
-       if (test_bit(This->ffbits, FF_TRIANGLE)) {
+       if (test_bit(This->joydev->ffbits, FF_TRIANGLE)) {
            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle);
            (*lpCallback)(&dei, pvRef);
        }
-       if (test_bit(This->ffbits, FF_SAW_UP)) {
+       if (test_bit(This->joydev->ffbits, FF_SAW_UP)) {
            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp);
            (*lpCallback)(&dei, pvRef);
        }
-       if (test_bit(This->ffbits, FF_SAW_DOWN)) {
+       if (test_bit(This->joydev->ffbits, FF_SAW_DOWN)) {
            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown);
            (*lpCallback)(&dei, pvRef);
        }
-    }
+    } 
 
     if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
-       && test_bit(This->ffbits, FF_RAMP)) {
+       && test_bit(This->joydev->ffbits, FF_RAMP)) {
         IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce);
         (*lpCallback)(&dei, pvRef);
     }
 
     if (type == DIEFT_ALL || type == DIEFT_CONDITION) {
-       if (test_bit(This->ffbits, FF_SPRING)) {
+       if (test_bit(This->joydev->ffbits, FF_SPRING)) {
            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring);
            (*lpCallback)(&dei, pvRef);
        }
-       if (test_bit(This->ffbits, FF_DAMPER)) {
+       if (test_bit(This->joydev->ffbits, FF_DAMPER)) {
            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper);
            (*lpCallback)(&dei, pvRef);
        }
-       if (test_bit(This->ffbits, FF_INERTIA)) {
+       if (test_bit(This->joydev->ffbits, FF_INERTIA)) {
            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia);
            (*lpCallback)(&dei, pvRef);
        }
-       if (test_bit(This->ffbits, FF_FRICTION)) {
+       if (test_bit(This->joydev->ffbits, FF_FRICTION)) {
            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction);
            (*lpCallback)(&dei, pvRef);
        }
     }
 
-    /* return to unaquired state if that's where it was */
-    if (xfd == -1)
-       IDirectInputDevice8_Unacquire(iface);
 #endif
 
     return DI_OK;
@@ -1439,70 +1284,66 @@ static HRESULT WINAPI JoystickWImpl_EnumEffects(LPDIRECTINPUTDEVICE8W iface,
     JoystickImpl* This = (JoystickImpl*)iface;
     int xfd = This->joyfd;
 
-    TRACE("(this=%p,%p,%ld) type=%ld fd=%d\n", This, pvRef, dwEffType, type, xfd);
+    TRACE("(this=%p,%p,%d) type=%d fd=%d\n", This, pvRef, dwEffType, type, xfd);
 
-    dei.dwSize = sizeof(DIEFFECTINFOW);
-
-    /* We need to return something even if we're not yet acquired */
-    if (xfd == -1)
-       IDirectInputDevice8_Acquire(iface);
+    dei.dwSize = sizeof(DIEFFECTINFOW);          
 
     if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
-       && test_bit(This->ffbits, FF_CONSTANT)) {
+       && test_bit(This->joydev->ffbits, FF_CONSTANT)) {
        IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce);
        (*lpCallback)(&dei, pvRef);
     }
 
     if ((type == DIEFT_ALL || type == DIEFT_PERIODIC)
-       && test_bit(This->ffbits, FF_PERIODIC)) {
-       if (test_bit(This->ffbits, FF_SQUARE)) {
+       && test_bit(This->joydev->ffbits, FF_PERIODIC)) {
+       if (test_bit(This->joydev->ffbits, FF_SQUARE)) {
            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Square);
            (*lpCallback)(&dei, pvRef);
        }
-       if (test_bit(This->ffbits, FF_SINE)) {
+       if (test_bit(This->joydev->ffbits, FF_SINE)) {
             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine);
            (*lpCallback)(&dei, pvRef);
        }
-       if (test_bit(This->ffbits, FF_TRIANGLE)) {
+       if (test_bit(This->joydev->ffbits, FF_TRIANGLE)) {
            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle);
            (*lpCallback)(&dei, pvRef);
        }
-       if (test_bit(This->ffbits, FF_SAW_UP)) {
+       if (test_bit(This->joydev->ffbits, FF_SAW_UP)) {
            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp);
            (*lpCallback)(&dei, pvRef);
        }
-       if (test_bit(This->ffbits, FF_SAW_DOWN)) {
+       if (test_bit(This->joydev->ffbits, FF_SAW_DOWN)) {
            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown);
            (*lpCallback)(&dei, pvRef);
        }
-    }
+    } 
 
     if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
-       && test_bit(This->ffbits, FF_RAMP)) {
+       && test_bit(This->joydev->ffbits, FF_RAMP)) {
         IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce);
         (*lpCallback)(&dei, pvRef);
     }
 
     if (type == DIEFT_ALL || type == DIEFT_CONDITION) {
-       if (test_bit(This->ffbits, FF_SPRING)) {
+       if (test_bit(This->joydev->ffbits, FF_SPRING)) {
            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring);
            (*lpCallback)(&dei, pvRef);
        }
-       if (test_bit(This->ffbits, FF_DAMPER)) {
+       if (test_bit(This->joydev->ffbits, FF_DAMPER)) {
            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper);
            (*lpCallback)(&dei, pvRef);
        }
-       if (test_bit(This->ffbits, FF_INERTIA)) {
+       if (test_bit(This->joydev->ffbits, FF_INERTIA)) {
            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia);
            (*lpCallback)(&dei, pvRef);
        }
-       if (test_bit(This->ffbits, FF_FRICTION)) {
+       if (test_bit(This->joydev->ffbits, FF_FRICTION)) {
            IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction);
            (*lpCallback)(&dei, pvRef);
        }
     }
 
-    /* return to unaquired state if that's where it was */
+    /* return to unacquired state if that's where it was */
     if (xfd == -1)
        IDirectInputDevice8_Unacquire(iface);
 #endif
@@ -1511,7 +1352,7 @@ static HRESULT WINAPI JoystickWImpl_EnumEffects(LPDIRECTINPUTDEVICE8W iface,
 }
 
 /*******************************************************************************
- *      GetEffectInfo - Get information about a particular effect
+ *      GetEffectInfo - Get information about a particular effect 
  */
 static HRESULT WINAPI JoystickAImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8A iface,
                                                  LPDIEFFECTINFOA pdei,
@@ -1522,7 +1363,7 @@ static HRESULT WINAPI JoystickAImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8A iface,
     TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
 
 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
-    return linuxinput_get_info_A(This->joyfd, guid, pdei);
+    return linuxinput_get_info_A(This->joyfd, guid, pdei); 
 #else
     return DI_OK;
 #endif
@@ -1533,9 +1374,9 @@ static HRESULT WINAPI JoystickWImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8W iface,
                                                   REFGUID guid)
 {
     JoystickImpl* This = (JoystickImpl*)iface;
-
+            
     TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
-
+        
 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
     return linuxinput_get_info_W(This->joyfd, guid, pdei);
 #else
@@ -1544,7 +1385,7 @@ static HRESULT WINAPI JoystickWImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8W iface,
 }
 
 /*******************************************************************************
- *      GetForceFeedbackState - Get information about the device's FF state
+ *      GetForceFeedbackState - Get information about the device's FF state 
  */
 static HRESULT WINAPI JoystickAImpl_GetForceFeedbackState(
        LPDIRECTINPUTDEVICE8A iface,
@@ -1573,33 +1414,42 @@ static HRESULT WINAPI JoystickAImpl_SendForceFeedbackCommand(
        DWORD dwFlags)
 {
     JoystickImpl* This = (JoystickImpl*)iface;
-    TRACE("(this=%p,%ld)\n", This, dwFlags);
+    TRACE("(this=%p,%d)\n", This, dwFlags);
 
 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
-    if (dwFlags == DISFFC_STOPALL) {
+    switch (dwFlags)
+    {
+    case DISFFC_STOPALL:
+    {
        /* Stop all effects */
-       EffectListItem* itr = This->top_effect;
-       while (itr) {
-           IDirectInputEffect_Stop(itr->ref);
-           itr = itr->next;
-       }
-    } else if (dwFlags == DISFFC_RESET) {
+        effect_list_item *itr;
+
+        LIST_FOR_EACH_ENTRY(itr, &This->ff_effects, effect_list_item, entry)
+            IDirectInputEffect_Stop(itr->ref);
+        break;
+    }
+
+    case DISFFC_RESET:
+    {
+        effect_list_item *itr, *ptr;
+
        /* Stop, unload, release and free all effects */
        /* This returns the device to its "bare" state */
-       while (This->top_effect) {
-           EffectListItem* temp = This->top_effect;
-           IDirectInputEffect_Stop(temp->ref);
-           IDirectInputEffect_Unload(temp->ref);
-           IDirectInputEffect_Release(temp->ref);
-           This->top_effect = temp->next;
-           free(temp);
-       }
-    } else if (dwFlags == DISFFC_PAUSE || dwFlags == DISFFC_CONTINUE) {
+        LIST_FOR_EACH_ENTRY_SAFE(itr, ptr, &This->ff_effects, effect_list_item, entry)
+            IDirectInputEffect_Release(itr->ref);
+        break;
+    }
+    case DISFFC_PAUSE:
+    case DISFFC_CONTINUE:
        FIXME("No support for Pause or Continue in linux\n");
-    } else if (dwFlags == DISFFC_SETACTUATORSOFF
-               || dwFlags == DISFFC_SETACTUATORSON) {
-       FIXME("No direct actuator control in linux\n");
-    } else {
+        break;
+
+    case DISFFC_SETACTUATORSOFF:
+    case DISFFC_SETACTUATORSON:
+        FIXME("No direct actuator control in linux\n");
+        break;
+
+    default:
        FIXME("Unknown Force Feedback Command!\n");
        return DIERR_INVALIDPARAM;
     }
@@ -1620,22 +1470,57 @@ static HRESULT WINAPI JoystickAImpl_EnumCreatedEffectObjects(
        DWORD dwFlags)
 {
     /* this function is safe to call on non-ff-enabled builds */
-
     JoystickImpl* This = (JoystickImpl*)iface;
-    EffectListItem* itr = This->top_effect;
-    TRACE("(this=%p,%p,%p,%ld)\n", This, lpCallback, pvRef, dwFlags);
+    effect_list_item *itr, *ptr;
+
+    TRACE("(this=%p,%p,%p,%d)\n", This, lpCallback, pvRef, dwFlags);
 
     if (!lpCallback)
        return DIERR_INVALIDPARAM;
 
     if (dwFlags != 0)
-       FIXME("Flags specified, but no flags exist yet (DX9)!");
+       FIXME("Flags specified, but no flags exist yet (DX9)!\n");
 
-    while (itr) {
-       (*lpCallback)(itr->ref, pvRef);
-       itr = itr->next;
-    }
+    LIST_FOR_EACH_ENTRY_SAFE(itr, ptr, &This->ff_effects, effect_list_item, entry)
+        (*lpCallback)(itr->ref, pvRef);
+
+    return DI_OK;
+}
+
+/******************************************************************************
+  *     GetDeviceInfo : get information about a device's identity
+  */
+static HRESULT WINAPI JoystickAImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8A iface,
+                                                  LPDIDEVICEINSTANCEA pdidi)
+{
+    JoystickImpl *This = (JoystickImpl *)iface;
+
+    TRACE("(%p) %p\n", This, pdidi);
+
+    if (pdidi == NULL) return E_POINTER;
+    if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3A)) &&
+        (pdidi->dwSize != sizeof(DIDEVICEINSTANCEA)))
+        return DIERR_INVALIDPARAM;
+
+    fill_joystick_dideviceinstanceA(pdidi, This->base.dinput->dwVersion,
+                                    get_joystick_index(&This->base.guid));
+    return DI_OK;
+}
+
+static HRESULT WINAPI JoystickWImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8W iface,
+                                                  LPDIDEVICEINSTANCEW pdidi)
+{
+    JoystickImpl *This = (JoystickImpl *)iface;
+
+    TRACE("(%p) %p\n", This, pdidi);
+
+    if (pdidi == NULL) return E_POINTER;
+    if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3W)) &&
+        (pdidi->dwSize != sizeof(DIDEVICEINSTANCEW)))
+        return DIERR_INVALIDPARAM;
 
+    fill_joystick_dideviceinstanceW(pdidi, This->base.dinput->dwVersion,
+                                    get_joystick_index(&This->base.guid));
     return DI_OK;
 }
 
@@ -1643,20 +1528,20 @@ static const IDirectInputDevice8AVtbl JoystickAvt =
 {
        IDirectInputDevice2AImpl_QueryInterface,
        IDirectInputDevice2AImpl_AddRef,
-       JoystickAImpl_Release,
+        IDirectInputDevice2AImpl_Release,
        JoystickAImpl_GetCapabilities,
-       JoystickAImpl_EnumObjects,
+        IDirectInputDevice2AImpl_EnumObjects,
        JoystickAImpl_GetProperty,
        JoystickAImpl_SetProperty,
        JoystickAImpl_Acquire,
        JoystickAImpl_Unacquire,
        JoystickAImpl_GetDeviceState,
-       JoystickAImpl_GetDeviceData,
-       JoystickAImpl_SetDataFormat,
-       JoystickAImpl_SetEventNotification,
+       IDirectInputDevice2AImpl_GetDeviceData,
+        IDirectInputDevice2AImpl_SetDataFormat,
+       IDirectInputDevice2AImpl_SetEventNotification,
        IDirectInputDevice2AImpl_SetCooperativeLevel,
-       IDirectInputDevice2AImpl_GetObjectInfo,
-       IDirectInputDevice2AImpl_GetDeviceInfo,
+        JoystickAImpl_GetObjectInfo,
+       JoystickAImpl_GetDeviceInfo,
        IDirectInputDevice2AImpl_RunControlPanel,
        IDirectInputDevice2AImpl_Initialize,
        JoystickAImpl_CreateEffect,
@@ -1685,20 +1570,20 @@ static const IDirectInputDevice8WVtbl JoystickWvt =
 {
        IDirectInputDevice2WImpl_QueryInterface,
        XCAST(AddRef)IDirectInputDevice2AImpl_AddRef,
-       XCAST(Release)JoystickAImpl_Release,
+        XCAST(Release)IDirectInputDevice2AImpl_Release,
        XCAST(GetCapabilities)JoystickAImpl_GetCapabilities,
-       JoystickWImpl_EnumObjects,
+        IDirectInputDevice2WImpl_EnumObjects,
        XCAST(GetProperty)JoystickAImpl_GetProperty,
        XCAST(SetProperty)JoystickAImpl_SetProperty,
        XCAST(Acquire)JoystickAImpl_Acquire,
        XCAST(Unacquire)JoystickAImpl_Unacquire,
        XCAST(GetDeviceState)JoystickAImpl_GetDeviceState,
-       XCAST(GetDeviceData)JoystickAImpl_GetDeviceData,
-       XCAST(SetDataFormat)JoystickAImpl_SetDataFormat,
-       XCAST(SetEventNotification)JoystickAImpl_SetEventNotification,
+       XCAST(GetDeviceData)IDirectInputDevice2AImpl_GetDeviceData,
+        XCAST(SetDataFormat)IDirectInputDevice2AImpl_SetDataFormat,
+       XCAST(SetEventNotification)IDirectInputDevice2AImpl_SetEventNotification,
        XCAST(SetCooperativeLevel)IDirectInputDevice2AImpl_SetCooperativeLevel,
-       IDirectInputDevice2WImpl_GetObjectInfo,
-       IDirectInputDevice2WImpl_GetDeviceInfo,
+        JoystickWImpl_GetObjectInfo,
+       JoystickWImpl_GetDeviceInfo,
        XCAST(RunControlPanel)IDirectInputDevice2AImpl_RunControlPanel,
        XCAST(Initialize)IDirectInputDevice2AImpl_Initialize,
        XCAST(CreateEffect)JoystickAImpl_CreateEffect,
index f8bc71a..47536f7 100644 (file)
@@ -17,7 +17,7 @@
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #include "config.h"
@@ -46,122 +46,79 @@ static const IDirectInputDevice8WVtbl SysKeyboardWvt;
 typedef struct SysKeyboardImpl SysKeyboardImpl;
 struct SysKeyboardImpl
 {
-        const void                     *lpVtbl;
-        LONG                            ref;
-        GUID                            guid;
-
-       IDirectInputImpl*               dinput;
-
-       HANDLE  hEvent;
-        /* SysKeyboardAImpl */
-       int                             acquired;
-        int                             buffersize;  /* set in 'SetProperty'         */
-        LPDIDEVICEOBJECTDATA            buffer;      /* buffer for 'GetDeviceData'.
-                                                        Alloc at 'Acquire', Free at
-                                                        'Unacquire'                  */
-        int                             count;       /* number of objects in use in
-                                                        'buffer'                     */
-        int                             start;       /* 'buffer' rotates. This is the
-                                                        first in use (if count > 0)  */
-        BOOL                            overflow;    /* return DI_BUFFEROVERFLOW in
-                                                        'GetDeviceData'              */
-        CRITICAL_SECTION                crit;
+    struct IDirectInputDevice2AImpl base;
+    BYTE DInputKeyState[WINE_DINPUT_KEYBOARD_MAX_KEYS];
 };
 
-static SysKeyboardImpl* current_lock = NULL;
-/* Today's acquired device
- * FIXME: currently this can be only one.
- * Maybe this should be a linked list or st.
- * I don't know what the rules are for multiple acquired keyboards,
- * but 'DI_LOSTFOCUS' and 'DI_UNACQUIRED' exist for a reason.
-*/
-
-static BYTE DInputKeyState[WINE_DINPUT_KEYBOARD_MAX_KEYS]; /* array for 'GetDeviceState' */
-
-static CRITICAL_SECTION keyboard_crit;
-static CRITICAL_SECTION_DEBUG critsect_debug =
+static BYTE map_dik_code(DWORD scanCode, DWORD vkCode)
 {
-    0, 0, &keyboard_crit,
-    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
-      0, 0, { (DWORD_PTR)(__FILE__ ": keyboard_crit") }
-};
-static CRITICAL_SECTION keyboard_crit = { &critsect_debug, -1, 0, 0, 0, 0 };
-
-static LONG keyboard_users = 0;
-static HHOOK keyboard_hook = NULL;
+    static const BYTE asciiCodes[] =
+     {/*32*/  DIK_SPACE,0,0,0,0,0,0,DIK_APOSTROPHE,
+      /*40*/  0,0,0,0,DIK_COMMA,DIK_MINUS,DIK_PERIOD,DIK_SLASH,
+      /*48*/  DIK_0,DIK_1,DIK_2,DIK_3,DIK_4,DIK_5,DIK_6,DIK_7,
+      /*56*/  DIK_8,DIK_9,DIK_COLON,DIK_SEMICOLON,0,DIK_EQUALS,0,0,
+      /*64*/  DIK_AT,DIK_A,DIK_B,DIK_C,DIK_D,DIK_E,DIK_F,DIK_G,
+      /*72*/  DIK_H,DIK_I,DIK_J,DIK_K,DIK_L,DIK_M,DIK_N,DIK_O,
+      /*80*/  DIK_P,DIK_Q,DIK_R,DIK_S,DIK_T,DIK_U,DIK_V,DIK_W,
+      /*88*/  DIK_X,DIK_Y,DIK_Z,DIK_LBRACKET,0,DIK_RBRACKET,DIK_CIRCUMFLEX,DIK_UNDERLINE}      /*95*/ ;
+
+    BYTE out_code = 0;
+    WCHAR c = MapVirtualKeyW(vkCode,MAPVK_VK_TO_CHAR);
+
+    if (c > 31 && c < 96)
+        out_code = asciiCodes[c - 32];
+
+    if (out_code == 0)
+        out_code = scanCode;
+
+    return out_code;
+}
 
-LRESULT CALLBACK KeyboardCallback( int code, WPARAM wparam, LPARAM lparam )
+static void KeyboardCallback( LPDIRECTINPUTDEVICE8A iface, WPARAM wparam, LPARAM lparam )
 {
-  BYTE dik_code;
-  BOOL down;
-  DWORD timestamp;
-  KBDLLHOOKSTRUCT *hook = (KBDLLHOOKSTRUCT *)lparam;
-  BYTE new_diks;
-
-  TRACE("(%d,%d,%ld)\n", code, wparam, lparam);
-
-  /** returns now if not HC_ACTION */
-  if (code != HC_ACTION) return CallNextHookEx(keyboard_hook, code, wparam, lparam);
-
-  {
-    dik_code = hook->scanCode;
-    if (hook->flags & LLKHF_EXTENDED) dik_code |= 0x80;
-    down = !(hook->flags & LLKHF_UP);
-    timestamp = hook->time;
-  }
-
-  /** returns now if key event already known */
-  new_diks = (down ? 0x80 : 0);
-  /*if (new_diks != DInputKeyState[dik_code]) return CallNextHookEx(keyboard_hook, code, wparam, lparam); TO BE FIXED */
-
-  DInputKeyState[dik_code] = new_diks;
-  TRACE(" setting %02X to %02X\n", dik_code, DInputKeyState[dik_code]);
-
-  if (current_lock != NULL) {
-    if (current_lock->hEvent) SetEvent(current_lock->hEvent);
-
-    if (current_lock->buffer != NULL) {
-      int n;
-
-      EnterCriticalSection(&(current_lock->crit));
-
-      n = (current_lock->start + current_lock->count) % current_lock->buffersize;
-
-      current_lock->buffer[n].dwOfs = dik_code;
-      current_lock->buffer[n].dwData = down ? 0x80 : 0;
-      current_lock->buffer[n].dwTimeStamp = timestamp;
-      current_lock->buffer[n].dwSequence = current_lock->dinput->evsequence++;
-
-      TRACE("Adding event at offset %d : %ld - %ld - %ld - %ld\n", n,
-           current_lock->buffer[n].dwOfs, current_lock->buffer[n].dwData, current_lock->buffer[n].dwTimeStamp, current_lock->buffer[n].dwSequence);
-
-      if (current_lock->count == current_lock->buffersize) {
-       current_lock->start = ++current_lock->start % current_lock->buffersize;
-       current_lock->overflow = TRUE;
-      } else
-       current_lock->count++;
-
-      LeaveCriticalSection(&(current_lock->crit));
-    }
-  }
-  return CallNextHookEx(keyboard_hook, code, wparam, lparam);
+    SysKeyboardImpl *This = (SysKeyboardImpl *)iface;
+    int dik_code;
+    KBDLLHOOKSTRUCT *hook = (KBDLLHOOKSTRUCT *)lparam;
+    BYTE new_diks;
+
+    if (wparam != WM_KEYDOWN && wparam != WM_KEYUP &&
+        wparam != WM_SYSKEYDOWN && wparam != WM_SYSKEYUP)
+        return;
+
+    TRACE("(%p) %ld,%ld\n", iface, wparam, lparam);
+
+    dik_code = map_dik_code(hook->scanCode & 0xff,hook->vkCode);
+    /* R-Shift is special - it is an extended key with separate scan code */
+    if (hook->flags & LLKHF_EXTENDED && dik_code != 0x36)
+        dik_code |= 0x80;
+
+    new_diks = hook->flags & LLKHF_UP ? 0 : 0x80;
+
+    /* returns now if key event already known */
+    if (new_diks == This->DInputKeyState[dik_code])
+        return;
+
+    This->DInputKeyState[dik_code] = new_diks;
+    TRACE(" setting %02X to %02X\n", dik_code, This->DInputKeyState[dik_code]);
+      
+    dik_code = id_to_offset(&This->base.data_format, DIDFT_MAKEINSTANCE(dik_code) | DIDFT_PSHBUTTON);
+    EnterCriticalSection(&This->base.crit);
+    queue_event((LPDIRECTINPUTDEVICE8A)This, dik_code, new_diks, hook->time, This->base.dinput->evsequence++);
+    LeaveCriticalSection(&This->base.crit);
 }
 
-static GUID DInput_Wine_Keyboard_GUID = { /* 0ab8648a-7735-11d2-8c73-71df54a96441 */
-  0x0ab8648a,
-  0x7735,
-  0x11d2,
-  {0x8c, 0x73, 0x71, 0xdf, 0x54, 0xa9, 0x64, 0x41}
+const GUID DInput_Wine_Keyboard_GUID = { /* 0ab8648a-7735-11d2-8c73-71df54a96441 */
+    0x0ab8648a, 0x7735, 0x11d2, {0x8c, 0x73, 0x71, 0xdf, 0x54, 0xa9, 0x64, 0x41}
 };
 
 static void fill_keyboard_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD version) {
     DWORD dwSize;
     DIDEVICEINSTANCEA ddi;
-
+    
     dwSize = lpddi->dwSize;
 
-    TRACE("%ld %p\n", dwSize, lpddi);
-
+    TRACE("%d %p\n", dwSize, lpddi);
+    
     memset(lpddi, 0, dwSize);
     memset(&ddi, 0, sizeof(ddi));
 
@@ -181,14 +138,14 @@ static void fill_keyboard_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD ver
 static void fill_keyboard_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD version) {
     DWORD dwSize;
     DIDEVICEINSTANCEW ddi;
-
+    
     dwSize = lpddi->dwSize;
 
-    TRACE("%ld %p\n", dwSize, lpddi);
-
+    TRACE("%d %p\n", dwSize, lpddi);
+    
     memset(lpddi, 0, dwSize);
     memset(&ddi, 0, sizeof(ddi));
-
     ddi.dwSize = dwSize;
     ddi.guidInstance = GUID_SysKeyboard;/* DInput's GUID */
     ddi.guidProduct = DInput_Wine_Keyboard_GUID; /* Vendor's GUID */
@@ -201,7 +158,7 @@ static void fill_keyboard_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD ver
 
     memcpy(lpddi, &ddi, (dwSize < sizeof(ddi) ? dwSize : sizeof(ddi)));
 }
-
 static BOOL keyboarddev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
 {
   if (id != 0)
@@ -211,9 +168,9 @@ static BOOL keyboarddev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEI
       ((dwDevType == DIDEVTYPE_KEYBOARD) && (version < 0x0800)) ||
       (((dwDevType == DI8DEVCLASS_KEYBOARD) || (dwDevType == DI8DEVTYPE_KEYBOARD)) && (version >= 0x0800))) {
     TRACE("Enumerating the Keyboard device\n");
-
     fill_keyboard_dideviceinstanceA(lpddi, version);
-
+    
     return TRUE;
   }
 
@@ -231,7 +188,7 @@ static BOOL keyboarddev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEI
     TRACE("Enumerating the Keyboard device\n");
 
     fill_keyboard_dideviceinstanceW(lpddi, version);
-
+    
     return TRUE;
   }
 
@@ -241,23 +198,44 @@ static BOOL keyboarddev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEI
 static SysKeyboardImpl *alloc_device(REFGUID rguid, const void *kvt, IDirectInputImpl *dinput)
 {
     SysKeyboardImpl* newDevice;
-    DWORD kbd_users;
+    LPDIDATAFORMAT df = NULL;
+    int i, idx = 0;
 
     newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(SysKeyboardImpl));
-    newDevice->lpVtbl = kvt;
-    newDevice->ref = 1;
-    memcpy(&(newDevice->guid),rguid,sizeof(*rguid));
-    newDevice->dinput = dinput;
-    InitializeCriticalSection(&(newDevice->crit));
-
-    EnterCriticalSection(&keyboard_crit);
-    kbd_users = InterlockedIncrement(&keyboard_users);
-    if (1 == kbd_users) {
-      keyboard_hook = SetWindowsHookExW( WH_KEYBOARD_LL, KeyboardCallback, DINPUT_instance, 0 );
+    newDevice->base.lpVtbl = kvt;
+    newDevice->base.ref = 1;
+    memcpy(&newDevice->base.guid, rguid, sizeof(*rguid));
+    newDevice->base.dinput = dinput;
+    newDevice->base.event_proc = KeyboardCallback;
+    InitializeCriticalSection(&newDevice->base.crit);
+    newDevice->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": SysKeyboardImpl*->base.crit");
+
+    /* Create copy of default data format */
+    if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIKeyboard.dwSize))) goto failed;
+    memcpy(df, &c_dfDIKeyboard, c_dfDIKeyboard.dwSize);
+    if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto failed;
+
+    for (i = 0; i < df->dwNumObjs; i++)
+    {
+        char buf[MAX_PATH];
+
+        if (!GetKeyNameTextA(((i & 0x7f) << 16) | ((i & 0x80) << 17), buf, sizeof(buf)))
+            continue;
+
+        memcpy(&df->rgodf[idx], &c_dfDIKeyboard.rgodf[i], df->dwObjSize);
+        df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
     }
-    LeaveCriticalSection(&keyboard_crit);
+    df->dwNumObjs = idx;
 
+    newDevice->base.data_format.wine_df = df;
+    IDirectInput_AddRef((LPDIRECTINPUTDEVICE8A)newDevice->base.dinput);
     return newDevice;
+
+failed:
+    if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
+    HeapFree(GetProcessHeap(), 0, df);
+    HeapFree(GetProcessHeap(), 0, newDevice);
+    return NULL;
 }
 
 
@@ -272,6 +250,7 @@ static HRESULT keyboarddev_create_deviceA(IDirectInputImpl *dinput, REFGUID rgui
        IsEqualGUID(&IID_IDirectInputDevice8A,riid)) {
       *pdev = (IDirectInputDeviceA*) alloc_device(rguid, &SysKeyboardAvt, dinput);
       TRACE("Creating a Keyboard device (%p)\n", *pdev);
+      if (!*pdev) return DIERR_OUTOFMEMORY;
       return DI_OK;
     } else
       return DIERR_NOINTERFACE;
@@ -290,6 +269,7 @@ static HRESULT keyboarddev_create_deviceW(IDirectInputImpl *dinput, REFGUID rgui
        IsEqualGUID(&IID_IDirectInputDevice8W,riid)) {
       *pdev = (IDirectInputDeviceW*) alloc_device(rguid, &SysKeyboardWvt, dinput);
       TRACE("Creating a Keyboard device (%p)\n", *pdev);
+      if (!*pdev) return DIERR_OUTOFMEMORY;
       return DI_OK;
     } else
       return DIERR_NOINTERFACE;
@@ -305,306 +285,36 @@ const struct dinput_device keyboard_device = {
   keyboarddev_create_deviceW
 };
 
-static ULONG WINAPI SysKeyboardAImpl_Release(LPDIRECTINPUTDEVICE8A iface)
-{
-       SysKeyboardImpl *This = (SysKeyboardImpl *)iface;
-       ULONG ref;
-       DWORD kbd_users;
-
-       ref = InterlockedDecrement(&(This->ref));
-       if (ref)
-               return ref;
-
-       EnterCriticalSection(&keyboard_crit);
-       kbd_users = InterlockedDecrement(&keyboard_users);
-       if (0 == kbd_users) {
-           UnhookWindowsHookEx( keyboard_hook );
-           keyboard_hook = 0;
-       }
-       LeaveCriticalSection(&keyboard_crit);
-
-       /* Free the data queue */
-       HeapFree(GetProcessHeap(),0,This->buffer);
-
-       DeleteCriticalSection(&(This->crit));
-
-       HeapFree(GetProcessHeap(),0,This);
-       return DI_OK;
-}
-
-static HRESULT WINAPI SysKeyboardAImpl_SetProperty(
-       LPDIRECTINPUTDEVICE8A iface,REFGUID rguid,LPCDIPROPHEADER ph
-)
-{
-       SysKeyboardImpl *This = (SysKeyboardImpl *)iface;
-
-       TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
-       TRACE("(size=%ld,headersize=%ld,obj=%ld,how=%ld\n",
-            ph->dwSize,ph->dwHeaderSize,ph->dwObj,ph->dwHow);
-       if (!HIWORD(rguid)) {
-               switch (LOWORD(rguid)) {
-               case (DWORD) DIPROP_BUFFERSIZE: {
-                       LPCDIPROPDWORD  pd = (LPCDIPROPDWORD)ph;
-
-                       TRACE("(buffersize=%ld)\n",pd->dwData);
-
-                        if (This->acquired)
-                           return DIERR_INVALIDPARAM;
-
-                        This->buffersize = pd->dwData;
-
-                       break;
-               }
-               default:
-                       WARN("Unknown type %p\n",rguid);
-                       break;
-               }
-       }
-       return DI_OK;
-}
-
-static HRESULT WINAPI SysKeyboardAImpl_GetProperty(
-       LPDIRECTINPUTDEVICE8A iface,REFGUID rguid,LPDIPROPHEADER ph
-)
-{
-       SysKe