[DINPUT] Sync with Wine Staging 4.18. CORE-16441
authorAmine Khaldi <amine.khaldi@reactos.org>
Sat, 26 Oct 2019 12:03:34 +0000 (13:03 +0100)
committerAmine Khaldi <amine.khaldi@reactos.org>
Sat, 26 Oct 2019 12:03:34 +0000 (13:03 +0100)
16 files changed:
dll/directx/wine/dinput/CMakeLists.txt
dll/directx/wine/dinput/config.c
dll/directx/wine/dinput/device.c
dll/directx/wine/dinput/device_private.h
dll/directx/wine/dinput/dinput_main.c
dll/directx/wine/dinput/dinput_private.h
dll/directx/wine/dinput/effect_linuxinput.c
dll/directx/wine/dinput/joystick.c
dll/directx/wine/dinput/joystick_linux.c
dll/directx/wine/dinput/joystick_linuxinput.c
dll/directx/wine/dinput/joystick_osx.c
dll/directx/wine/dinput/joystick_private.h
dll/directx/wine/dinput/keyboard.c
dll/directx/wine/dinput/mouse.c
dll/directx/wine/dinput/precomp.h
media/doc/README.WINE

index 7244903..638312f 100644 (file)
@@ -1,5 +1,5 @@
 
-add_definitions(-D__WINESRC__)
+add_definitions(-D__WINESRC__ -DDIRECTINPUT_VERSION=0x0700)
 include_directories(${REACTOS_SOURCE_DIR}/sdk/include/reactos/wine)
 spec2def(dinput.dll dinput.spec ADD_IMPORTLIB)
 
index 885f1f4..b8a280a 100644 (file)
 
 #define NONAMELESSUNION
 
+
 #include "wine/unicode.h"
 #include "objbase.h"
 #include "dinput_private.h"
 #include "device_private.h"
 #include "resource.h"
 
+#include "wine/heap.h"
+
 typedef struct {
     int nobjects;
     IDirectInputDevice8W *lpdid;
     DIDEVICEINSTANCEW ddi;
     DIDEVICEOBJECTINSTANCEW ddo[256];
+    /* ActionFormat for every user.
+     * In same order as ConfigureDevicesData usernames */
+    DIACTIONFORMATW *user_afs;
 } DeviceData;
 
 typedef struct {
@@ -38,10 +44,11 @@ typedef struct {
 
 typedef struct {
     IDirectInput8W *lpDI;
-    LPDIACTIONFORMATW lpdiaf;
     LPDIACTIONFORMATW original_lpdiaf;
     DIDevicesData devices_data;
     int display_only;
+    int nusernames;
+    WCHAR **usernames;
 } ConfigureDevicesData;
 
 /*
@@ -57,27 +64,43 @@ static BOOL CALLBACK collect_objects(LPCDIDEVICEOBJECTINSTANCEW lpddo, LPVOID pv
     return DIENUM_CONTINUE;
 }
 
-static BOOL CALLBACK count_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
+static BOOL CALLBACK collect_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
 {
-    DIDevicesData *data = (DIDevicesData*) pvRef;
+    ConfigureDevicesData *data = (ConfigureDevicesData*) pvRef;
+    DeviceData *device;
+    int i, j;
 
-    data->ndevices++;
-    return DIENUM_CONTINUE;
-}
+    IDirectInputDevice_AddRef(lpdid);
 
-static BOOL CALLBACK collect_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
-{
-    DIDevicesData *data = (DIDevicesData*) pvRef;
-    DeviceData *device = &data->devices[data->ndevices];
+    /* alloc array for devices if this is our first device */
+    if (!data->devices_data.ndevices)
+        data->devices_data.devices = HeapAlloc(GetProcessHeap(), 0, sizeof(DeviceData) * (dwRemaining + 1));
+    device = &data->devices_data.devices[data->devices_data.ndevices];
     device->lpdid = lpdid;
     device->ddi = *lpddi;
 
-    IDirectInputDevice_AddRef(lpdid);
-
     device->nobjects = 0;
     IDirectInputDevice_EnumObjects(lpdid, collect_objects, (LPVOID) device, DIDFT_ALL);
 
-    data->ndevices++;
+    device->user_afs = heap_alloc(sizeof(*device->user_afs) * data->nusernames);
+    memset(device->user_afs, 0, sizeof(*device->user_afs) * data->nusernames);
+    for (i = 0; i < data->nusernames; i++)
+    {
+        DIACTIONFORMATW *user_af = &device->user_afs[i];
+        user_af->dwNumActions = data->original_lpdiaf->dwNumActions;
+        user_af->guidActionMap = data->original_lpdiaf->guidActionMap;
+        user_af->rgoAction = heap_alloc(sizeof(DIACTIONW) * data->original_lpdiaf->dwNumActions);
+        memset(user_af->rgoAction, 0, sizeof(DIACTIONW) * data->original_lpdiaf->dwNumActions);
+        for (j = 0; j < user_af->dwNumActions; j++)
+        {
+            user_af->rgoAction[j].dwSemantic = data->original_lpdiaf->rgoAction[j].dwSemantic;
+            user_af->rgoAction[j].dwFlags = data->original_lpdiaf->rgoAction[j].dwFlags;
+            user_af->rgoAction[j].u.lptszActionName = data->original_lpdiaf->rgoAction[j].u.lptszActionName;
+        }
+        IDirectInputDevice8_BuildActionMap(lpdid, user_af, data->usernames[i], 0);
+    }
+
+    data->devices_data.ndevices++;
     return DIENUM_CONTINUE;
 }
 
@@ -170,10 +193,18 @@ static DeviceData* get_cur_device(HWND dialog)
     return &data->devices_data.devices[sel];
 }
 
-static LPDIACTIONFORMATW get_cur_lpdiaf(HWND dialog)
+static DIACTIONFORMATW *get_cur_lpdiaf(HWND dialog)
 {
     ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
-    return data->lpdiaf;
+    int controller_sel = SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_GETCURSEL, 0, 0);
+    int player_sel = SendDlgItemMessageW(dialog, IDC_PLAYERCOMBO, CB_GETCURSEL, 0, 0);
+    return &data->devices_data.devices[controller_sel].user_afs[player_sel];
+}
+
+static DIACTIONFORMATW *get_original_lpdiaf(HWND dialog)
+{
+    ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
+    return data->original_lpdiaf;
 }
 
 static int dialog_display_only(HWND dialog)
@@ -182,40 +213,36 @@ static int dialog_display_only(HWND dialog)
     return data->display_only;
 }
 
-static void init_devices(HWND dialog, IDirectInput8W *lpDI, DIDevicesData *data, LPDIACTIONFORMATW lpdiaf)
+static void init_devices(HWND dialog, ConfigureDevicesData *data)
 {
     int i;
 
-    /* Count devices */
-    data->ndevices = 0;
-    IDirectInput8_EnumDevicesBySemantics(lpDI, NULL, lpdiaf, count_devices, (LPVOID) data, 0);
-
-    /* Allocate devices */
-    data->devices = HeapAlloc(GetProcessHeap(), 0, sizeof(DeviceData) * data->ndevices);
-
     /* Collect and insert */
-    data->ndevices = 0;
-    IDirectInput8_EnumDevicesBySemantics(lpDI, NULL, lpdiaf, collect_devices, (LPVOID) data, 0);
+    data->devices_data.ndevices = 0;
+    IDirectInput8_EnumDevicesBySemantics(data->lpDI, NULL, data->original_lpdiaf, collect_devices, (LPVOID) data, 0);
 
-    for (i=0; i < data->ndevices; i++)
-        SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_ADDSTRING, 0, (LPARAM) data->devices[i].ddi.tszProductName );
+    for (i = 0; i < data->devices_data.ndevices; i++)
+        SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_ADDSTRING, 0, (LPARAM) data->devices_data.devices[i].ddi.tszProductName );
+    for (i = 0; i < data->nusernames; i++)
+        SendDlgItemMessageW(dialog, IDC_PLAYERCOMBO, CB_ADDSTRING, 0, (LPARAM) data->usernames[i]);
 }
 
 static void destroy_data(HWND dialog)
 {
-    int i;
+    int i, j;
     ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
     DIDevicesData *devices_data = &data->devices_data;
 
     /* Free the devices */
     for (i=0; i < devices_data->ndevices; i++)
+    {
         IDirectInputDevice8_Release(devices_data->devices[i].lpdid);
+        for (j=0; j < data->nusernames; j++)
+            heap_free(devices_data->devices[i].user_afs[j].rgoAction);
+        heap_free(devices_data->devices[i].user_afs);
+    }
 
     HeapFree(GetProcessHeap(), 0, devices_data->devices);
-
-    /* Free the backup LPDIACTIONFORMATW  */
-    HeapFree(GetProcessHeap(), 0, data->original_lpdiaf->rgoAction);
-    HeapFree(GetProcessHeap(), 0, data->original_lpdiaf);
 }
 
 static void fill_device_object_list(HWND dialog)
@@ -231,6 +258,7 @@ static void fill_device_object_list(HWND dialog)
     /* Add each object */
     for (i=0; i < device->nobjects; i++)
     {
+        DWORD ddo_inst, ddo_type;
         int action = -1;
 
         item.mask = LVIF_TEXT | LVIF_PARAM;
@@ -241,12 +269,20 @@ static void fill_device_object_list(HWND dialog)
 
         /* Add the item */
         SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTITEMW, 0, (LPARAM) &item);
+        ddo_inst = DIDFT_GETINSTANCE(device->ddo[i].dwType);
+        ddo_type = DIDFT_GETTYPE(device->ddo[i].dwType);
 
-        /* Search for an assigned action  for this device */
+        /* Search for an assigned action for this device */
         for (j=0; j < lpdiaf->dwNumActions; j++)
         {
+            DWORD af_inst = DIDFT_GETINSTANCE(lpdiaf->rgoAction[j].dwObjID);
+            DWORD af_type = DIDFT_GETTYPE(lpdiaf->rgoAction[j].dwObjID);
+            if (af_type == DIDFT_PSHBUTTON) af_type = DIDFT_BUTTON;
+            if (af_type == DIDFT_RELAXIS) af_type = DIDFT_AXIS;
+            /* NOTE previously compared dwType == dwObjId but default buildActionMap actions
+             * were PSHBUTTON and RELAXS and didnt show up on config */
             if (IsEqualGUID(&lpdiaf->rgoAction[j].guidInstance, &device->ddi.guidInstance) &&
-                lpdiaf->rgoAction[j].dwObjID == device->ddo[i].dwType)
+                ddo_inst == af_inst && ddo_type & af_type)
             {
                 action = j;
                 break;
@@ -260,7 +296,7 @@ static void fill_device_object_list(HWND dialog)
 static void show_suitable_actions(HWND dialog)
 {
     DeviceData *device = get_cur_device(dialog);
-    LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog);
+    LPDIACTIONFORMATW lpdiaf = get_original_lpdiaf(dialog);
     int i, added = 0;
     int obj = lv_get_cur_item(dialog);
 
@@ -298,10 +334,13 @@ static void assign_action(HWND dialog)
     int obj = lv_get_cur_item(dialog);
     int old_action = lv_get_item_data(dialog, obj);
     int used_obj;
-
-    DIDEVICEOBJECTINSTANCEW ddo = device->ddo[obj];
+    DWORD type;
 
     if (old_action == action) return;
+    if (obj < 0) return;
+    if (lpdiaf->rgoAction[old_action].dwFlags & DIA_APPFIXED) return;
+
+    type = device->ddo[obj].dwType;
 
     /* Clear old action */
     if (old_action != -1)
@@ -320,7 +359,7 @@ static void assign_action(HWND dialog)
     lv_set_action(dialog, used_obj, -1, lpdiaf);
 
     /* Set new action */
-    lpdiaf->rgoAction[action].dwObjID = ddo.dwType;
+    lpdiaf->rgoAction[action].dwObjID = type;
     lpdiaf->rgoAction[action].guidInstance = device->ddi.guidInstance;
     lpdiaf->rgoAction[action].dwHow = DIAH_USERCONFIG;
 
@@ -328,24 +367,35 @@ static void assign_action(HWND dialog)
     lv_set_action(dialog, obj, action, lpdiaf);
 }
 
-static void copy_actions(LPDIACTIONFORMATW to, LPDIACTIONFORMATW from)
+static void reset_actions(HWND dialog)
 {
-    DWORD i;
-    for (i=0; i < from->dwNumActions; i++)
+    ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
+    DIDevicesData *ddata = (DIDevicesData*) &data->devices_data;
+    unsigned i, j;
+
+    for (i = 0; i < data->devices_data.ndevices; i++)
     {
-        to->rgoAction[i].guidInstance = from->rgoAction[i].guidInstance;
-        to->rgoAction[i].dwObjID = from->rgoAction[i].dwObjID;
-        to->rgoAction[i].dwHow = from->rgoAction[i].dwHow;
-        to->rgoAction[i].u.lptszActionName = from->rgoAction[i].u.lptszActionName;
+        DeviceData *device = &ddata->devices[i];
+        for (j = 0; j < data->nusernames; j++)
+            IDirectInputDevice8_BuildActionMap(device->lpdid, &device->user_afs[j], data->usernames[j], DIDBAM_HWDEFAULTS);
     }
 }
 
-static void reset_actions(HWND dialog)
-{
+static void save_actions(HWND dialog) {
     ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
-    LPDIACTIONFORMATW to = data->lpdiaf, from = data->original_lpdiaf;
-
-    copy_actions(to, from);
+    DIDevicesData *ddata = (DIDevicesData*) &data->devices_data;
+    unsigned i, j;
+    if (!data->display_only) {
+        for (i = 0; i < ddata->ndevices; i++)
+        {
+            DeviceData *device = &ddata->devices[i];
+            for (j = 0; j < data->nusernames; j++)
+            {
+                if (save_mapping_settings(device->lpdid, &device->user_afs[j], data->usernames[j]) != DI_OK)
+                    MessageBoxA(dialog, "Could not save settings", 0, MB_ICONERROR);
+            }
+        }
+    }
 }
 
 static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
@@ -357,26 +407,27 @@ static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM w
             ConfigureDevicesData *data = (ConfigureDevicesData*) lParam;
 
             /* Initialize action format and enumerate devices */
-            init_devices(dialog, data->lpDI, &data->devices_data, data->lpdiaf);
+            init_devices(dialog, data);
 
             /* Store information in the window */
             SetWindowLongPtrW(dialog, DWLP_USER, (LONG_PTR) data);
 
             init_listview_columns(dialog);
 
-            /* Create a backup action format for CANCEL and RESET operations */
-            data->original_lpdiaf = HeapAlloc(GetProcessHeap(), 0, sizeof(*data->original_lpdiaf));
-            data->original_lpdiaf->dwNumActions = data->lpdiaf->dwNumActions;
-            data->original_lpdiaf->rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*data->lpdiaf->dwNumActions);
-            copy_actions(data->original_lpdiaf, data->lpdiaf);
-
             /* Select the first device and show its actions */
             SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_SETCURSEL, 0, 0);
+            SendDlgItemMessageW(dialog, IDC_PLAYERCOMBO, CB_SETCURSEL, 0, 0);
             fill_device_object_list(dialog);
 
+            ShowCursor(TRUE);
+
             break;
         }
 
+        case WM_DESTROY:
+            ShowCursor(FALSE);
+            break;
+
         case WM_NOTIFY:
 
             switch (((LPNMHDR)lParam)->code)
@@ -407,6 +458,7 @@ static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM w
                     break;
 
                 case IDC_CONTROLLERCOMBO:
+                case IDC_PLAYERCOMBO:
 
                     switch (HIWORD(wParam))
                     {
@@ -417,12 +469,12 @@ static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM w
                     break;
 
                 case IDOK:
+                    save_actions(dialog);
                     EndDialog(dialog, 0);
                     destroy_data(dialog);
                     break;
 
                 case IDCANCEL:
-                    reset_actions(dialog);
                     EndDialog(dialog, 0);
                     destroy_data(dialog);
                     break;
@@ -445,15 +497,48 @@ HRESULT _configure_devices(IDirectInput8W *iface,
                            LPVOID pvRefData
 )
 {
+    int i;
+    DWORD size;
+    WCHAR *username = NULL;
     ConfigureDevicesData data;
     data.lpDI = iface;
-    data.lpdiaf = lpdiCDParams->lprgFormats;
+    data.original_lpdiaf = lpdiCDParams->lprgFormats;
     data.display_only = !(dwFlags & DICD_EDIT);
+    data.nusernames = lpdiCDParams->dwcUsers;
+    if (lpdiCDParams->lptszUserNames == NULL)
+    {
+        /* Get default user name */
+        GetUserNameW(NULL, &size);
+        username = heap_alloc(size * sizeof(WCHAR) );
+        GetUserNameW(username, &size);
+        data.nusernames = 1;
+        data.usernames = heap_alloc(sizeof(WCHAR *));
+        data.usernames[0] = username;
+    }
+    else
+    {
+        WCHAR *p = lpdiCDParams->lptszUserNames;
+        data.usernames = heap_alloc(sizeof(WCHAR *) * data.nusernames);
+        for (i = 0; i < data.nusernames; i++)
+        {
+            if (*p)
+            {
+                data.usernames[i] = p;
+                while (*(p++));
+            }
+            else
+                /* Return if there is an empty string */
+                return DIERR_INVALIDPARAM;
+        }
+    }
 
     InitCommonControls();
 
     DialogBoxParamW(DINPUT_instance, (const WCHAR *)MAKEINTRESOURCE(IDD_CONFIGUREDEVICES),
             lpdiCDParams->hwnd, ConfigureDevicesDlgProc, (LPARAM)&data);
 
+    heap_free(username);
+    heap_free(data.usernames);
+
     return DI_OK;
 }
index 6c44616..df7a22a 100644 (file)
@@ -30,6 +30,7 @@
 #include <string.h>
 #include "wine/debug.h"
 #include "wine/unicode.h"
+#include "wine/heap.h"
 #include "windef.h"
 #include "winbase.h"
 #include "winreg.h"
@@ -643,12 +644,29 @@ static DWORD semantic_to_obj_id(IDirectInputDeviceImpl* This, DWORD dwSemantic)
     return type | (0x0000ff00 & (obj_instance << 8));
 }
 
+static void del_mapping_key(const WCHAR *device, const WCHAR *username, const WCHAR *guid) {
+    static const WCHAR subkey[] = {
+        'S','o','f','t','w','a','r','e','\\',
+        'W','i','n','e','\\',
+        'D','i','r','e','c','t','I','n','p','u','t','\\',
+        'M','a','p','p','i','n','g','s','\\','%','s','\\','%','s','\\','%','s','\0'};
+    WCHAR *keyname;
+
+    keyname = heap_alloc(sizeof(WCHAR) * (lstrlenW(subkey) + strlenW(username) + strlenW(device) + strlenW(guid)));
+    sprintfW(keyname, subkey, username, device, guid);
+
+    /* Remove old key mappings so there will be no overlapping mappings */
+    RegDeleteKeyW(HKEY_CURRENT_USER, keyname);
+
+    heap_free(keyname);
+}
+
 /*
  * get_mapping_key
  * Retrieves an open registry key to save the mapping, parametrized for an username,
  * specific device and specific action mapping guid.
  */
-static HKEY get_mapping_key(const WCHAR *device, const WCHAR *username, const WCHAR *guid)
+static HKEY get_mapping_key(const WCHAR *device, const WCHAR *username, const WCHAR *guid, BOOL create)
 {
     static const WCHAR subkey[] = {
         'S','o','f','t','w','a','r','e','\\',
@@ -663,15 +681,18 @@ static HKEY get_mapping_key(const WCHAR *device, const WCHAR *username, const WC
     sprintfW(keyname, subkey, username, device, guid);
 
     /* The key used is HKCU\Software\Wine\DirectInput\Mappings\[username]\[device]\[mapping_guid] */
-    if (RegCreateKeyW(HKEY_CURRENT_USER, keyname, &hkey))
-        hkey = 0;
+    if (create) {
+        if (RegCreateKeyW(HKEY_CURRENT_USER, keyname, &hkey))
+            hkey = 0;
+    } else if (RegOpenKeyW(HKEY_CURRENT_USER, keyname, &hkey))
+            hkey = 0;
 
     HeapFree(GetProcessHeap(), 0, keyname);
 
     return hkey;
 }
 
-static HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUsername)
+HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUsername)
 {
     WCHAR *guid_str = NULL;
     DIDEVICEINSTANCEW didev;
@@ -684,7 +705,9 @@ static HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORM
     if (StringFromCLSID(&lpdiaf->guidActionMap, &guid_str) != S_OK)
         return DI_SETTINGSNOTSAVED;
 
-    hkey = get_mapping_key(didev.tszInstanceName, lpszUsername, guid_str);
+    del_mapping_key(didev.tszInstanceName, lpszUsername, guid_str);
+
+    hkey = get_mapping_key(didev.tszInstanceName, lpszUsername, guid_str, TRUE);
 
     if (!hkey)
     {
@@ -714,12 +737,12 @@ static HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORM
     return DI_OK;
 }
 
-static BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMATW lpdiaf, const WCHAR *username)
+BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMATW lpdiaf, const WCHAR *username)
 {
     HKEY hkey;
     WCHAR *guid_str;
     DIDEVICEINSTANCEW didev;
-    int i, mapped = 0;
+    int i;
 
     didev.dwSize = sizeof(didev);
     IDirectInputDevice8_GetDeviceInfo(&This->IDirectInputDevice8W_iface, &didev);
@@ -727,7 +750,7 @@ static BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMAT
     if (StringFromCLSID(&lpdiaf->guidActionMap, &guid_str) != S_OK)
         return FALSE;
 
-    hkey = get_mapping_key(didev.tszInstanceName, username, guid_str);
+    hkey = get_mapping_key(didev.tszInstanceName, username, guid_str, FALSE);
 
     if (!hkey)
     {
@@ -748,15 +771,21 @@ static BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMAT
         {
             lpdiaf->rgoAction[i].dwObjID = id;
             lpdiaf->rgoAction[i].guidInstance = didev.guidInstance;
-            lpdiaf->rgoAction[i].dwHow = DIAH_DEFAULT;
-            mapped += 1;
+            lpdiaf->rgoAction[i].dwHow = DIAH_USERCONFIG;
+        }
+        else
+        {
+            memset(&lpdiaf->rgoAction[i].guidInstance, 0, sizeof(GUID));
+            lpdiaf->rgoAction[i].dwHow = DIAH_UNMAPPED;
         }
+
     }
 
     RegCloseKey(hkey);
     CoTaskMemFree(guid_str);
 
-    return mapped > 0;
+    /* On Windows BuildActionMap can open empty mapping, so always return TRUE if get_mapping_key is success */
+    return TRUE;
 }
 
 HRESULT _build_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUserName, DWORD dwFlags, DWORD devMask, LPCDIDATAFORMAT df)
@@ -779,13 +808,18 @@ HRESULT _build_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf,
         load_success = load_mapping_settings(This, lpdiaf, username);
     }
 
-    if (load_success) return DI_OK;
+    if (load_success) {
+        /* Update dwCRC to track if action format has changed */
+        for (i=0; i < lpdiaf->dwNumActions; i++)
+        {
+            lpdiaf->dwCRC ^= (lpdiaf->rgoAction[i].dwObjID << i * 2) | (lpdiaf->rgoAction[i].dwObjID >> (sizeof(lpdiaf->dwCRC) * 8 - i * 2));
+            lpdiaf->dwCRC ^= (lpdiaf->rgoAction[i].dwSemantic << (i * 2 + 5)) | (lpdiaf->rgoAction[i].dwSemantic >> (sizeof(lpdiaf->dwCRC) * 8 - (i * 2 + 5)));
+        }
+        return DI_OK;
+    }
 
     for (i=0; i < lpdiaf->dwNumActions; i++)
     {
-        /* Don't touch a user configured action */
-        if (lpdiaf->rgoAction[i].dwHow == DIAH_USERCONFIG) continue;
-
         if ((lpdiaf->rgoAction[i].dwSemantic & devMask) == devMask)
         {
             DWORD obj_id = semantic_to_obj_id(This, lpdiaf->rgoAction[i].dwSemantic);
@@ -816,6 +850,14 @@ HRESULT _build_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf,
         }
     }
 
+    /* Update dwCRC to track if action format has changed */
+    lpdiaf->dwCRC = 0;
+    for (i=0; i < lpdiaf->dwNumActions; i++)
+    {
+        lpdiaf->dwCRC ^= (lpdiaf->rgoAction[i].dwObjID << i * 2) | (lpdiaf->rgoAction[i].dwObjID >> (sizeof(lpdiaf->dwCRC) * 8 - i * 2));
+        lpdiaf->dwCRC ^= (lpdiaf->rgoAction[i].dwSemantic << (i * 2 + 5)) | (lpdiaf->rgoAction[i].dwSemantic >> (sizeof(lpdiaf->dwCRC) * 8 - (i * 2 + 5)));
+    }
+
     if (!has_actions) return DI_NOEFFECT;
 
     return  IDirectInputDevice8WImpl_BuildActionMap(iface, lpdiaf, lpszUserName, dwFlags);
@@ -831,6 +873,7 @@ HRESULT _set_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, L
     DIPROPSTRING dps;
     WCHAR username[MAX_PATH];
     DWORD username_size = MAX_PATH;
+    DWORD new_crc = 0;
     int i, action = 0, num_actions = 0;
     unsigned int offset = 0;
 
@@ -841,12 +884,30 @@ HRESULT _set_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, L
     data_format.dwFlags = DIDF_RELAXIS;
     data_format.dwDataSize = lpdiaf->dwDataSize;
 
+    /* Calculate checksum for actionformat */
+    for (i=0; i < lpdiaf->dwNumActions; i++)
+    {
+        new_crc ^= (lpdiaf->rgoAction[i].dwObjID << i * 2) | (lpdiaf->rgoAction[i].dwObjID >> (sizeof(lpdiaf->dwCRC) * 8 - i * 2));
+        new_crc ^= (lpdiaf->rgoAction[i].dwSemantic << (i * 2 + 5)) | (lpdiaf->rgoAction[i].dwSemantic >> (sizeof(lpdiaf->dwCRC) * 8 - (i * 2 + 5)));
+    }
+
     /* Count the actions */
     for (i=0; i < lpdiaf->dwNumActions; i++)
-        if (IsEqualGUID(&This->guid, &lpdiaf->rgoAction[i].guidInstance))
+    {
+        if (IsEqualGUID(&This->guid, &lpdiaf->rgoAction[i].guidInstance) ||
+                (IsEqualGUID(&IID_NULL, &lpdiaf->rgoAction[i].guidInstance) && 
+                  ((lpdiaf->rgoAction[i].dwSemantic & lpdiaf->dwGenre) == lpdiaf->dwGenre ||
+                   (lpdiaf->rgoAction[i].dwSemantic & 0xff000000) == 0xff000000 /* Any Axis */) ))
+        {
             num_actions++;
+        }
+    }
 
-    if (num_actions == 0) return DI_NOEFFECT;
+    /* Should return DI_NOEFFECT if we dont have any actions and actionformat has not changed */
+    if (num_actions == 0 && lpdiaf->dwCRC == new_crc && !(dwFlags & DIDSAM_FORCESAVE)) return DI_NOEFFECT;
+
+    /* update dwCRC to track if action format has changed */
+    lpdiaf->dwCRC = new_crc;
 
     This->num_actions = num_actions;
 
@@ -880,7 +941,39 @@ HRESULT _set_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, L
 
             action++;
         }
+        else if ((lpdiaf->rgoAction[i].dwSemantic & lpdiaf->dwGenre) == lpdiaf->dwGenre ||
+                 (lpdiaf->rgoAction[i].dwSemantic & 0xff000000) == 0xff000000 /* Any Axis */)
+        {
+            DWORD obj_id = semantic_to_obj_id(This, lpdiaf->rgoAction[i].dwSemantic);
+            DWORD type = DIDFT_GETTYPE(obj_id);
+            DWORD inst = DIDFT_GETINSTANCE(obj_id);
+            LPDIOBJECTDATAFORMAT obj;
+
+            if (type == DIDFT_PSHBUTTON) type = DIDFT_BUTTON;
+            else if (type == DIDFT_RELAXIS) type = DIDFT_AXIS;
+
+            obj = dataformat_to_odf_by_type(df, inst, type);
+            TRACE("obj %p, inst 0x%08x, type 0x%08x\n", obj, inst, type);
+            if(obj)
+            {
+                memcpy(&obj_df[action], obj, df->dwObjSize);
+
+                This->action_map[action].uAppData = lpdiaf->rgoAction[i].uAppData;
+                This->action_map[action].offset = offset;
+                obj_df[action].dwOfs = offset;
+                offset += (type & DIDFT_BUTTON) ? 1 : 4;
+
+                action++;
+            }
+        }
+    }
+
+    if (action == 0)
+    {
+        HeapFree(GetProcessHeap(), 0, obj_df);
+        return DI_NOEFFECT;
     }
+    data_format.dwNumObjs = action;
 
     IDirectInputDevice8_SetDataFormat(iface, &data_format);
 
@@ -1140,7 +1233,7 @@ ULONG WINAPI IDirectInputDevice2WImpl_Release(LPDIRECTINPUTDEVICE8W iface)
     IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
     ULONG ref = InterlockedDecrement(&(This->ref));
 
-    TRACE("(%p) releasing from %d\n", This, ref + 1);
+    TRACE("(%p) ref %d\n", This, ref);
 
     if (ref) return ref;
 
@@ -1168,7 +1261,7 @@ ULONG WINAPI IDirectInputDevice2WImpl_Release(LPDIRECTINPUTDEVICE8W iface)
 
     HeapFree(GetProcessHeap(), 0, This);
 
-    return DI_OK;
+    return ref;
 }
 
 ULONG WINAPI IDirectInputDevice2AImpl_Release(LPDIRECTINPUTDEVICE8A iface)
@@ -1181,7 +1274,7 @@ HRESULT WINAPI IDirectInputDevice2WImpl_QueryInterface(LPDIRECTINPUTDEVICE8W ifa
 {
     IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
 
-    TRACE("(%p this=%p,%s,%p)\n", iface, This, debugstr_guid(riid), ppobj);
+    TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppobj);
     if (IsEqualGUID(&IID_IUnknown, riid) ||
         IsEqualGUID(&IID_IDirectInputDeviceA,  riid) ||
         IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
@@ -1203,7 +1296,7 @@ HRESULT WINAPI IDirectInputDevice2WImpl_QueryInterface(LPDIRECTINPUTDEVICE8W ifa
     }
 
     WARN("Unsupported interface!\n");
-    return E_FAIL;
+    return E_NOINTERFACE;
 }
 
 HRESULT WINAPI IDirectInputDevice2AImpl_QueryInterface(LPDIRECTINPUTDEVICE8A iface, REFIID riid, LPVOID *ppobj)
@@ -1215,7 +1308,9 @@ HRESULT WINAPI IDirectInputDevice2AImpl_QueryInterface(LPDIRECTINPUTDEVICE8A ifa
 ULONG WINAPI IDirectInputDevice2WImpl_AddRef(LPDIRECTINPUTDEVICE8W iface)
 {
     IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
-    return InterlockedIncrement(&This->ref);
+    ULONG ref = InterlockedIncrement(&This->ref);
+    TRACE( "(%p) ref %d\n", This, ref );
+    return ref;
 }
 
 ULONG WINAPI IDirectInputDevice2AImpl_AddRef(LPDIRECTINPUTDEVICE8A iface)
@@ -1231,7 +1326,7 @@ HRESULT WINAPI IDirectInputDevice2AImpl_EnumObjects(LPDIRECTINPUTDEVICE8A iface,
     DIDEVICEOBJECTINSTANCEA ddoi;
     int i;
 
-    TRACE("(%p) %p,%p flags:%08x)\n", iface, lpCallback, lpvRef, dwFlags);
+    TRACE("(%p)->(%p,%p flags:%08x)\n", This, lpCallback, lpvRef, dwFlags);
     TRACE("  - flags = ");
     _dump_EnumObjects_flags(dwFlags);
     TRACE("\n");
@@ -1261,7 +1356,7 @@ HRESULT WINAPI IDirectInputDevice2WImpl_EnumObjects(LPDIRECTINPUTDEVICE8W iface,
     DIDEVICEOBJECTINSTANCEW ddoi;
     int i;
 
-    TRACE("(%p) %p,%p flags:%08x)\n", iface, lpCallback, lpvRef, dwFlags);
+    TRACE("(%p)->(%p,%p flags:%08x)\n", This, lpCallback, lpvRef, dwFlags);
     TRACE("  - flags = ");
     _dump_EnumObjects_flags(dwFlags);
     TRACE("\n");
@@ -1292,7 +1387,7 @@ HRESULT WINAPI IDirectInputDevice2WImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface,
 {
     IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
 
-    TRACE("(%p) %s,%p\n", iface, debugstr_guid(rguid), pdiph);
+    TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(rguid), pdiph);
     _dump_DIPROPHEADER(pdiph);
 
     if (!IS_DIPROP(rguid)) return DI_OK;
@@ -1357,7 +1452,7 @@ HRESULT WINAPI IDirectInputDevice2WImpl_SetProperty(
 {
     IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
 
-    TRACE("(%p) %s,%p\n", iface, debugstr_guid(rguid), pdiph);
+    TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(rguid), pdiph);
     _dump_DIPROPHEADER(pdiph);
 
     if (!IS_DIPROP(rguid)) return DI_OK;
@@ -1596,7 +1691,8 @@ HRESULT WINAPI IDirectInputDevice2AImpl_GetDeviceData(LPDIRECTINPUTDEVICE8A ifac
 
 HRESULT WINAPI IDirectInputDevice2WImpl_RunControlPanel(LPDIRECTINPUTDEVICE8W iface, HWND hwndOwner, DWORD dwFlags)
 {
-    FIXME("(this=%p,%p,0x%08x): stub!\n", iface, hwndOwner, dwFlags);
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
+    FIXME("%p)->(%p,0x%08x): stub!\n", This, hwndOwner, dwFlags);
 
     return DI_OK;
 }
@@ -1610,7 +1706,8 @@ HRESULT WINAPI IDirectInputDevice2AImpl_RunControlPanel(LPDIRECTINPUTDEVICE8A if
 HRESULT WINAPI IDirectInputDevice2WImpl_Initialize(LPDIRECTINPUTDEVICE8W iface, HINSTANCE hinst, DWORD dwVersion,
                                                    REFGUID rguid)
 {
-    FIXME("(this=%p,%p,%d,%s): stub!\n", iface, hinst, dwVersion, debugstr_guid(rguid));
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
+    FIXME("(%p)->(%p,%d,%s): stub!\n", This, hinst, dwVersion, debugstr_guid(rguid));
     return DI_OK;
 }
 
@@ -1628,7 +1725,8 @@ HRESULT WINAPI IDirectInputDevice2AImpl_Initialize(LPDIRECTINPUTDEVICE8A iface,
 HRESULT WINAPI IDirectInputDevice2WImpl_CreateEffect(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPCDIEFFECT lpeff,
                                                      LPDIRECTINPUTEFFECT *ppdef, LPUNKNOWN pUnkOuter)
 {
-    FIXME("(this=%p,%s,%p,%p,%p): stub!\n", iface, debugstr_guid(rguid), lpeff, ppdef, pUnkOuter);
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
+    FIXME("(%p)->(%s,%p,%p,%p): stub!\n", This, debugstr_guid(rguid), lpeff, ppdef, pUnkOuter);
 
     FIXME("not available in the generic implementation\n");
     *ppdef = NULL;
@@ -1648,9 +1746,9 @@ HRESULT WINAPI IDirectInputDevice2AImpl_EnumEffects(
        LPVOID lpvRef,
        DWORD dwFlags)
 {
-    FIXME("(this=%p,%p,%p,0x%08x): stub!\n",
-         iface, lpCallback, lpvRef, dwFlags);
-    
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8A(iface);
+    FIXME("%p)->(%p,%p,0x%08x): stub!\n", This, lpCallback, lpvRef, dwFlags);
+
     return DI_OK;
 }
 
@@ -1660,9 +1758,9 @@ HRESULT WINAPI IDirectInputDevice2WImpl_EnumEffects(
        LPVOID lpvRef,
        DWORD dwFlags)
 {
-    FIXME("(this=%p,%p,%p,0x%08x): stub!\n",
-         iface, lpCallback, lpvRef, dwFlags);
-    
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
+    FIXME("(%p)->(%p,%p,0x%08x): stub!\n", This, lpCallback, lpvRef, dwFlags);
+
     return DI_OK;
 }
 
@@ -1671,8 +1769,8 @@ HRESULT WINAPI IDirectInputDevice2AImpl_GetEffectInfo(
        LPDIEFFECTINFOA lpdei,
        REFGUID rguid)
 {
-    FIXME("(this=%p,%p,%s): stub!\n",
-         iface, lpdei, debugstr_guid(rguid));
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8A(iface);
+    FIXME("(%p)->(%p,%s): stub!\n", This, lpdei, debugstr_guid(rguid));
     return DI_OK;
 }
 
@@ -1681,14 +1779,15 @@ HRESULT WINAPI IDirectInputDevice2WImpl_GetEffectInfo(
        LPDIEFFECTINFOW lpdei,
        REFGUID rguid)
 {
-    FIXME("(this=%p,%p,%s): stub!\n",
-         iface, lpdei, debugstr_guid(rguid));
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
+    FIXME("(%p)->(%p,%s): stub!\n", This, lpdei, debugstr_guid(rguid));
     return DI_OK;
 }
 
 HRESULT WINAPI IDirectInputDevice2WImpl_GetForceFeedbackState(LPDIRECTINPUTDEVICE8W iface, LPDWORD pdwOut)
 {
-    FIXME("(this=%p,%p): stub!\n", iface, pdwOut);
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
+    FIXME("(%p)->(%p): stub!\n", This, pdwOut);
     return DI_OK;
 }
 
@@ -1700,7 +1799,8 @@ HRESULT WINAPI IDirectInputDevice2AImpl_GetForceFeedbackState(LPDIRECTINPUTDEVIC
 
 HRESULT WINAPI IDirectInputDevice2WImpl_SendForceFeedbackCommand(LPDIRECTINPUTDEVICE8W iface, DWORD dwFlags)
 {
-    TRACE("(%p) 0x%08x:\n", iface, dwFlags);
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
+    TRACE("(%p)->(0x%08x)\n", This, dwFlags);
     return DI_NOEFFECT;
 }
 
@@ -1713,7 +1813,8 @@ HRESULT WINAPI IDirectInputDevice2AImpl_SendForceFeedbackCommand(LPDIRECTINPUTDE
 HRESULT WINAPI IDirectInputDevice2WImpl_EnumCreatedEffectObjects(LPDIRECTINPUTDEVICE8W iface,
         LPDIENUMCREATEDEFFECTOBJECTSCALLBACK lpCallback, LPVOID lpvRef, DWORD dwFlags)
 {
-    FIXME("(this=%p,%p,%p,0x%08x): stub!\n", iface, lpCallback, lpvRef, dwFlags);
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
+    FIXME("(%p)0>(%p,%p,0x%08x): stub!\n", This, lpCallback, lpvRef, dwFlags);
     return DI_OK;
 }
 
@@ -1726,7 +1827,8 @@ HRESULT WINAPI IDirectInputDevice2AImpl_EnumCreatedEffectObjects(LPDIRECTINPUTDE
 
 HRESULT WINAPI IDirectInputDevice2WImpl_Escape(LPDIRECTINPUTDEVICE8W iface, LPDIEFFESCAPE lpDIEEsc)
 {
-    FIXME("(this=%p,%p): stub!\n", iface, lpDIEEsc);
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
+    FIXME("(%p)->(%p): stub!\n", This, lpDIEEsc);
     return DI_OK;
 }
 
@@ -1756,7 +1858,8 @@ HRESULT WINAPI IDirectInputDevice2WImpl_SendDeviceData(LPDIRECTINPUTDEVICE8W ifa
                                                        LPCDIDEVICEOBJECTDATA rgdod, LPDWORD pdwInOut,
                                                        DWORD dwFlags)
 {
-    FIXME("(this=%p,0x%08x,%p,%p,0x%08x): stub!\n", iface, cbObjectData, rgdod, pdwInOut, dwFlags);
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
+    FIXME("(%p)->(0x%08x,%p,%p,0x%08x): stub!\n", This, cbObjectData, rgdod, pdwInOut, dwFlags);
 
     return DI_OK;
 }
@@ -1776,7 +1879,8 @@ HRESULT WINAPI IDirectInputDevice7AImpl_EnumEffectsInFile(LPDIRECTINPUTDEVICE8A
                                                          LPVOID pvRef,
                                                          DWORD dwFlags)
 {
-    FIXME("(%p)->(%s,%p,%p,%08x): stub !\n", iface, lpszFileName, pec, pvRef, dwFlags);
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8A(iface);
+    FIXME("(%p)->(%s,%p,%p,%08x): stub !\n", This, lpszFileName, pec, pvRef, dwFlags);
     
     return DI_OK;
 }
@@ -1787,7 +1891,8 @@ HRESULT WINAPI IDirectInputDevice7WImpl_EnumEffectsInFile(LPDIRECTINPUTDEVICE8W
                                                          LPVOID pvRef,
                                                          DWORD dwFlags)
 {
-    FIXME("(%p)->(%s,%p,%p,%08x): stub !\n", iface, debugstr_w(lpszFileName), pec, pvRef, dwFlags);
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
+    FIXME("(%p)->(%s,%p,%p,%08x): stub !\n", This, debugstr_w(lpszFileName), pec, pvRef, dwFlags);
     
     return DI_OK;
 }
@@ -1798,7 +1903,8 @@ HRESULT WINAPI IDirectInputDevice7AImpl_WriteEffectToFile(LPDIRECTINPUTDEVICE8A
                                                          LPDIFILEEFFECT rgDiFileEft,
                                                          DWORD dwFlags)
 {
-    FIXME("(%p)->(%s,%08x,%p,%08x): stub !\n", iface, lpszFileName, dwEntries, rgDiFileEft, dwFlags);
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8A(iface);
+    FIXME("(%p)->(%s,%08x,%p,%08x): stub !\n", This, lpszFileName, dwEntries, rgDiFileEft, dwFlags);
     
     return DI_OK;
 }
@@ -1809,7 +1915,8 @@ HRESULT WINAPI IDirectInputDevice7WImpl_WriteEffectToFile(LPDIRECTINPUTDEVICE8W
                                                          LPDIFILEEFFECT rgDiFileEft,
                                                          DWORD dwFlags)
 {
-    FIXME("(%p)->(%s,%08x,%p,%08x): stub !\n", iface, debugstr_w(lpszFileName), dwEntries, rgDiFileEft, dwFlags);
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
+    FIXME("(%p)->(%s,%08x,%p,%08x): stub !\n", This, debugstr_w(lpszFileName), dwEntries, rgDiFileEft, dwFlags);
     
     return DI_OK;
 }
@@ -1819,7 +1926,8 @@ HRESULT WINAPI IDirectInputDevice8WImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W ifa
                                                       LPCWSTR lpszUserName,
                                                       DWORD dwFlags)
 {
-    FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
+    FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", This, lpdiaf, debugstr_w(lpszUserName), dwFlags);
 #define X(x) if (dwFlags & x) FIXME("\tdwFlags =|"#x"\n");
        X(DIDBAM_DEFAULT)
        X(DIDBAM_PRESERVE)
@@ -1833,7 +1941,8 @@ HRESULT WINAPI IDirectInputDevice8WImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W ifa
 HRESULT WINAPI IDirectInputDevice8AImpl_GetImageInfo(LPDIRECTINPUTDEVICE8A iface,
                                                     LPDIDEVICEIMAGEINFOHEADERA lpdiDevImageInfoHeader)
 {
-    FIXME("(%p)->(%p): stub !\n", iface, lpdiDevImageInfoHeader);
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8A(iface);
+    FIXME("(%p)->(%p): stub !\n", This, lpdiDevImageInfoHeader);
     
     return DI_OK;
 }
@@ -1841,7 +1950,8 @@ HRESULT WINAPI IDirectInputDevice8AImpl_GetImageInfo(LPDIRECTINPUTDEVICE8A iface
 HRESULT WINAPI IDirectInputDevice8WImpl_GetImageInfo(LPDIRECTINPUTDEVICE8W iface,
                                                     LPDIDEVICEIMAGEINFOHEADERW lpdiDevImageInfoHeader)
 {
-    FIXME("(%p)->(%p): stub !\n", iface, lpdiDevImageInfoHeader);
+    IDirectInputDeviceImpl *This = impl_from_IDirectInputDevice8W(iface);
+    FIXME("(%p)->(%p): stub !\n", This, lpdiDevImageInfoHeader);
     
     return DI_OK;
 }
index d9e2997..114e397 100644 (file)
@@ -114,6 +114,8 @@ typedef struct
     LPDIRECTINPUTEFFECT ref;
 } effect_list_item;
 
+extern const GUID DInput_PIDVID_Product_GUID DECLSPEC_HIDDEN;
+
 /* Various debug tools */
 extern void _dump_DIPROPHEADER(LPCDIPROPHEADER diph)  DECLSPEC_HIDDEN;
 extern void _dump_OBJECTINSTANCEA(const DIDEVICEOBJECTINSTANCEA *ddoi)  DECLSPEC_HIDDEN;
@@ -123,6 +125,9 @@ extern const char *_dump_dinput_GUID(const GUID *guid)  DECLSPEC_HIDDEN;
 
 extern LPDIOBJECTDATAFORMAT dataformat_to_odf_by_type(LPCDIDATAFORMAT df, int n, DWORD type)   DECLSPEC_HIDDEN;
 
+extern HRESULT save_mapping_settings(IDirectInputDevice8W *iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUsername) DECLSPEC_HIDDEN;
+extern BOOL load_mapping_settings(IDirectInputDeviceImpl *This, LPDIACTIONFORMATW lpdiaf, const WCHAR *username) DECLSPEC_HIDDEN;
+
 extern HRESULT _build_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUserName, DWORD dwFlags, DWORD devMask, LPCDIDATAFORMAT df)  DECLSPEC_HIDDEN;
 extern HRESULT _set_action_map(LPDIRECTINPUTDEVICE8W iface, LPDIACTIONFORMATW lpdiaf, LPCWSTR lpszUserName, DWORD dwFlags, LPCDIDATAFORMAT df) DECLSPEC_HIDDEN;
 
index 4b05889..645d29d 100644 (file)
@@ -30,7 +30,6 @@
  * - Fallout : works great in X and DGA mode
  */
 
-#include "config.h"
 #include <assert.h>
 #include <stdarg.h>
 #include <string.h>
@@ -39,7 +38,9 @@
 #define NONAMELESSUNION
 
 #include "wine/debug.h"
+#include "wine/heap.h"
 #include "wine/unicode.h"
+#include "wine/asm.h"
 #include "windef.h"
 #include "winbase.h"
 #include "winuser.h"
@@ -47,6 +48,7 @@
 #include "objbase.h"
 #include "rpcproxy.h"
 #include "initguid.h"
+#include "devguid.h"
 #include "dinput_private.h"
 #include "device_private.h"
 #include "dinputd.h"
@@ -289,6 +291,20 @@ static void _dump_EnumDevices_dwFlags(DWORD dwFlags)
     TRACE("\n");
 }
 
+static const char *dump_semantic(DWORD semantic)
+{
+    if((semantic & 0xff000000) == 0xff000000)
+        return "Any AXIS";
+    else if((semantic & 0x82000000) == 0x82000000)
+        return "Mouse";
+    else if((semantic & 0x81000000) == 0x81000000)
+        return "Keybaord";
+    else if((semantic & DIVIRTUAL_FLYING_HELICOPTER) == DIVIRTUAL_FLYING_HELICOPTER)
+        return "Helicopter";
+
+    return "Unknown";
+}
+
 static void _dump_diactionformatA(LPDIACTIONFORMATA lpdiActionFormat)
 {
     unsigned int i;
@@ -311,7 +327,7 @@ static void _dump_diactionformatA(LPDIACTIONFORMATA lpdiActionFormat)
     {
         TRACE("diaf.rgoAction[%u]:\n", i);
         TRACE("\tuAppData=0x%lx\n", lpdiActionFormat->rgoAction[i].uAppData);
-        TRACE("\tdwSemantic=0x%08x\n", lpdiActionFormat->rgoAction[i].dwSemantic);
+        TRACE("\tdwSemantic=0x%08x (%s)\n", lpdiActionFormat->rgoAction[i].dwSemantic, dump_semantic(lpdiActionFormat->rgoAction[i].dwSemantic));
         TRACE("\tdwFlags=0x%x\n", lpdiActionFormat->rgoAction[i].dwFlags);
         TRACE("\tszActionName=%s\n", debugstr_a(lpdiActionFormat->rgoAction[i].u.lptszActionName));
         TRACE("\tguidInstance=%s\n", debugstr_guid(&lpdiActionFormat->rgoAction[i].guidInstance));
@@ -467,9 +483,10 @@ static HRESULT WINAPI IDirectInputAImpl_EnumDevices(
 
     for (i = 0; i < ARRAY_SIZE(dinput_devices); i++) {
         if (!dinput_devices[i]->enum_deviceA) continue;
+
+        TRACE(" Checking device %u ('%s')\n", i, dinput_devices[i]->name);
         for (j = 0, r = S_OK; SUCCEEDED(r); j++) {
             devInstance.dwSize = sizeof(devInstance);
-            TRACE("  - checking device %u ('%s')\n", i, dinput_devices[i]->name);
             r = dinput_devices[i]->enum_deviceA(dwDevType, dwFlags, &devInstance, This->dwVersion, j);
             if (r == S_OK)
                 if (enum_callback_wrapper(lpCallback, &devInstance, pvRef) == DIENUM_STOP)
@@ -525,7 +542,7 @@ static ULONG WINAPI IDirectInputAImpl_AddRef(LPDIRECTINPUT7A iface)
     IDirectInputImpl *This = impl_from_IDirectInput7A( iface );
     ULONG ref = InterlockedIncrement(&This->ref);
 
-    TRACE( "(%p) incrementing from %d\n", This, ref - 1);
+    TRACE( "(%p) ref %d\n", This, ref );
     return ref;
 }
 
@@ -540,7 +557,7 @@ static ULONG WINAPI IDirectInputAImpl_Release(LPDIRECTINPUT7A iface)
     IDirectInputImpl *This = impl_from_IDirectInput7A( iface );
     ULONG ref = InterlockedDecrement( &This->ref );
 
-    TRACE( "(%p) releasing from %d\n", This, ref + 1 );
+    TRACE( "(%p) ref %d\n", This, ref );
 
     if (ref == 0)
     {
@@ -566,53 +583,39 @@ static HRESULT WINAPI IDirectInputAImpl_QueryInterface(LPDIRECTINPUT7A iface, RE
     if (!riid || !ppobj)
         return E_POINTER;
 
+    *ppobj = NULL;
+
+#if DIRECTINPUT_VERSION == 0x0700
     if (IsEqualGUID( &IID_IUnknown, riid ) ||
-        IsEqualGUID( &IID_IDirectInputA,  riid ) ||
-        IsEqualGUID( &IID_IDirectInput2A, riid ) ||
-        IsEqualGUID( &IID_IDirectInput7A, riid ))
-    {
+         IsEqualGUID( &IID_IDirectInputA,  riid ) ||
+         IsEqualGUID( &IID_IDirectInput2A, riid ) ||
+         IsEqualGUID( &IID_IDirectInput7A, riid ))
         *ppobj = &This->IDirectInput7A_iface;
-        IUnknown_AddRef( (IUnknown*)*ppobj );
-
-        return DI_OK;
-    }
-
-    if (IsEqualGUID( &IID_IDirectInputW,  riid ) ||
-        IsEqualGUID( &IID_IDirectInput2W, riid ) ||
-        IsEqualGUID( &IID_IDirectInput7W, riid ))
-    {
+    else if (IsEqualGUID( &IID_IDirectInputW,  riid ) ||
+             IsEqualGUID( &IID_IDirectInput2W, riid ) ||
+             IsEqualGUID( &IID_IDirectInput7W, riid ))
         *ppobj = &This->IDirectInput7W_iface;
-        IUnknown_AddRef( (IUnknown*)*ppobj );
-
-        return DI_OK;
-    }
 
-    if (IsEqualGUID( &IID_IDirectInput8A, riid ))
-    {
+#else
+    if (IsEqualGUID( &IID_IUnknown, riid ) ||
+        IsEqualGUID( &IID_IDirectInput8A, riid ))
         *ppobj = &This->IDirectInput8A_iface;
-        IUnknown_AddRef( (IUnknown*)*ppobj );
-
-        return DI_OK;
-    }
 
-    if (IsEqualGUID( &IID_IDirectInput8W, riid ))
-    {
+    else if (IsEqualGUID( &IID_IDirectInput8W, riid ))
         *ppobj = &This->IDirectInput8W_iface;
-        IUnknown_AddRef( (IUnknown*)*ppobj );
 
-        return DI_OK;
-    }
+#endif
 
     if (IsEqualGUID( &IID_IDirectInputJoyConfig8, riid ))
-    {
         *ppobj = &This->IDirectInputJoyConfig8_iface;
-        IUnknown_AddRef( (IUnknown*)*ppobj );
 
+    if(*ppobj)
+    {
+        IUnknown_AddRef( (IUnknown*)*ppobj );
         return DI_OK;
     }
 
-    FIXME( "Unsupported interface: %s\n", debugstr_guid(riid));
-    *ppobj = NULL;
+    WARN( "Unsupported interface: %s\n", debugstr_guid(riid));
     return E_NOINTERFACE;
 }
 
@@ -690,7 +693,7 @@ static HRESULT WINAPI IDirectInputAImpl_Initialize(LPDIRECTINPUT7A iface, HINSTA
 {
     IDirectInputImpl *This = impl_from_IDirectInput7A( iface );
 
-    TRACE("(%p)->(%p, 0x%04x)\n", iface, hinst, version);
+    TRACE("(%p)->(%p, 0x%04x)\n", This, hinst, version);
 
     if (!hinst)
         return DIERR_INVALIDPARAM;
@@ -947,7 +950,7 @@ static HRESULT WINAPI IDirectInput8AImpl_Initialize(LPDIRECTINPUT8A iface, HINST
 {
     IDirectInputImpl *This = impl_from_IDirectInput8A( iface );
 
-    TRACE("(%p)->(%p, 0x%04x)\n", iface, hinst, version);
+    TRACE("(%p)->(%p, 0x%04x)\n", This, hinst, version);
 
     if (!hinst)
         return DIERR_INVALIDPARAM;
@@ -1104,10 +1107,12 @@ static HRESULT WINAPI IDirectInput8AImpl_EnumDevicesBySemantics(
 
         if (lpCallback(&didevis[i], lpdid, callbackFlags, --remain, pvRef) == DIENUM_STOP)
         {
+            IDirectInputDevice_Release(lpdid);
             HeapFree(GetProcessHeap(), 0, didevis);
             HeapFree(GetProcessHeap(), 0, username_w);
             return DI_OK;
         }
+        IDirectInputDevice_Release(lpdid);
     }
 
     HeapFree(GetProcessHeap(), 0, didevis);
@@ -1130,9 +1135,11 @@ static HRESULT WINAPI IDirectInput8AImpl_EnumDevicesBySemantics(
 
             if (lpCallback(&didevi, lpdid, callbackFlags, --remain, pvRef) == DIENUM_STOP)
             {
+                IDirectInputDevice_Release(lpdid);
                 HeapFree(GetProcessHeap(), 0, username_w);
                 return DI_OK;
             }
+            IDirectInputDevice_Release(lpdid);
         }
     }
 
@@ -1260,9 +1267,34 @@ static HRESULT WINAPI IDirectInput8AImpl_ConfigureDevices(
 
     /* Copy parameters */
     diCDParamsW.dwSize = sizeof(DICONFIGUREDEVICESPARAMSW);
+    diCDParamsW.dwcUsers = lpdiCDParams->dwcUsers;
     diCDParamsW.dwcFormats = lpdiCDParams->dwcFormats;
     diCDParamsW.lprgFormats = &diafW;
     diCDParamsW.hwnd = lpdiCDParams->hwnd;
+    diCDParamsW.lptszUserNames = NULL;
+
+    if (lpdiCDParams->lptszUserNames) {
+        char *start = lpdiCDParams->lptszUserNames;
+        WCHAR *to = NULL;
+        int total_len = 0;
+        for (i = 0; i < lpdiCDParams->dwcUsers; i++)
+        {
+            char *end = start + 1;
+            int len;
+            while (*(end++));
+            len = MultiByteToWideChar(CP_ACP, 0, start, end - start, NULL, 0);
+            total_len += len + 2; /* length of string and two null char */
+            if (to)
+                to = HeapReAlloc(GetProcessHeap(), 0, to, sizeof(WCHAR) * total_len);
+            else
+                to = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * total_len);
+
+            MultiByteToWideChar(CP_ACP, 0, start, end - start, to + (total_len - len - 2), len);
+            to[total_len] = 0;
+            to[total_len - 1] = 0;
+        }
+        diCDParamsW.lptszUserNames = to;
+    }
 
     diafW.rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*lpdiCDParams->lprgFormats->dwNumActions);
     _copy_diactionformatAtoW(&diafW, lpdiCDParams->lprgFormats);
@@ -1290,6 +1322,8 @@ static HRESULT WINAPI IDirectInput8AImpl_ConfigureDevices(
 
     HeapFree(GetProcessHeap(), 0, diafW.rgoAction);
 
+    heap_free((void*) diCDParamsW.lptszUserNames);
+
     return hr;
 }
 
@@ -1777,7 +1811,7 @@ static DWORD WINAPI hook_thread_proc(void *param)
         DispatchMessageW(&msg);
     }
 
-    return 0;
+    FreeLibraryAndExitThread(DINPUT_instance, 0);
 }
 
 static DWORD hook_thread_id;
@@ -1794,15 +1828,16 @@ static CRITICAL_SECTION dinput_hook_crit = { &dinput_critsect_debug, -1, 0, 0, 0
 static BOOL check_hook_thread(void)
 {
     static HANDLE hook_thread;
+    HMODULE module;
 
     EnterCriticalSection(&dinput_hook_crit);
 
     TRACE("IDirectInputs left: %d\n", list_count(&direct_input_list));
     if (!list_empty(&direct_input_list) && !hook_thread)
     {
+        GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)DINPUT_instance, &module);
         hook_thread_event = CreateEventW(NULL, FALSE, FALSE, NULL);
         hook_thread = CreateThread(NULL, 0, hook_thread_proc, hook_thread_event, 0, &hook_thread_id);
-        LeaveCriticalSection(&dinput_hook_crit);
     }
     else if (list_empty(&direct_input_list) && hook_thread)
     {
@@ -1817,16 +1852,11 @@ static BOOL check_hook_thread(void)
 
         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);
 
+    LeaveCriticalSection(&dinput_hook_crit);
     return hook_thread_id != 0;
 }
 
index acda6c8..289b2f1 100644 (file)
@@ -81,6 +81,7 @@ extern void _copy_diactionformatWtoA(LPDIACTIONFORMATA, LPDIACTIONFORMATW) DECLS
 extern HRESULT _configure_devices(IDirectInput8W *iface, LPDICONFIGUREDEVICESCALLBACK lpdiCallback, LPDICONFIGUREDEVICESPARAMSW lpdiCDParams, DWORD dwFlags, LPVOID pvRefData) DECLSPEC_HIDDEN;
 
 extern WCHAR* get_mapping_path(const WCHAR *device, const WCHAR *username) DECLSPEC_HIDDEN;
+extern DWORD get_device_type(DWORD version, BOOL is_joystick) DECLSPEC_HIDDEN;
 
 #define IS_DIPROP(x)    (((ULONG_PTR)(x) >> 16) == 0)
 
index 7477bc7..4b2f317 100644 (file)
@@ -167,7 +167,9 @@ static ULONG WINAPI LinuxInputEffectImpl_AddRef(
        LPDIRECTINPUTEFFECT iface)
 {
     LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
-    return InterlockedIncrement(&(This->ref));
+    ULONG ref = InterlockedIncrement(&This->ref);
+    TRACE( "(%p) ref %d\n", This, ref );
+    return ref;
 }
 
 static HRESULT WINAPI LinuxInputEffectImpl_Download(
@@ -759,6 +761,8 @@ static ULONG WINAPI LinuxInputEffectImpl_Release(LPDIRECTINPUTEFFECT iface)
     LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface);
     ULONG ref = InterlockedDecrement(&(This->ref));
 
+    TRACE( "(%p) ref %d\n", This, ref );
+
     if (ref == 0)
     {
         LinuxInputEffectImpl_Stop(iface);
index 0ec1ceb..9d7baf4 100644 (file)
 
 #include <stdio.h>
 
+#include "device_private.h"
 #include "joystick_private.h"
 #include "wine/debug.h"
+#include "wine/heap.h"
 #include "winreg.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
 
+#define VID_MICROSOFT 0x045e
+
+static const WORD PID_XBOX_CONTROLLERS[] =  {
+    0x0202, /* Xbox Controller */
+    0x0285, /* Xbox Controller S */
+    0x0289, /* Xbox Controller S */
+    0x028e, /* Xbox360 Controller */
+    0x028f, /* Xbox360 Wireless Controller */
+    0x02d1, /* Xbox One Controller */
+    0x02dd, /* Xbox One Controller (Covert Forces/Firmware 2015) */
+    0x02e0, /* Xbox One X Controller */
+    0x02e3, /* Xbox One Elite Controller */
+    0x02e6, /* Wireless XBox Controller Dongle */
+    0x02ea, /* Xbox One S Controller */
+    0x02fd, /* Xbox One S Controller (Firmware 2017) */
+    0x0719, /* Xbox 360 Wireless Adapter */
+};
+
+/* Windows uses this GUID for guidProduct on non-keyboard/mouse devices.
+ * Data1 contains the device VID (low word) and PID (high word).
+ * Data4 ends with the ASCII bytes "PIDVID".
+ */
+const GUID DInput_PIDVID_Product_GUID = { /* device_pidvid-0000-0000-0000-504944564944 */
+    0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44}
+};
+
 static inline JoystickGenericImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
 {
     return CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface), JoystickGenericImpl, base);
@@ -76,6 +104,16 @@ DWORD typeFromGUID(REFGUID guid)
     }
 }
 
+DWORD get_device_type(DWORD version, BOOL is_joystick)
+{
+    if (is_joystick)
+        return version >= 0x0800 ? DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8) :
+                    DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
+
+    return version >= 0x0800 ? DI8DEVTYPE_GAMEPAD | (DI8DEVTYPEJOYSTICK_STANDARD << 8) :
+                DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8);
+}
+
 static void _dump_DIEFFECT_flags(DWORD dwFlags)
 {
     if (TRACE_ON(dinput)) {
@@ -271,6 +309,89 @@ BOOL device_disabled_registry(const char* name)
     return do_disable;
 }
 
+BOOL is_xinput_device(const DIDEVCAPS *devcaps, WORD vid, WORD pid)
+{
+    int i;
+
+    if (vid == VID_MICROSOFT)
+    {
+        for (i = 0; i < ARRAY_SIZE(PID_XBOX_CONTROLLERS); i++)
+            if (pid == PID_XBOX_CONTROLLERS[i]) return TRUE;
+    }
+
+    return (devcaps->dwAxes == 6 && devcaps->dwButtons >= 14);
+}
+
+static void remap_init(JoystickGenericImpl *This, int obj, ObjProps *remap_props)
+{
+    /* Configure as if nothing changed so the helper functions can only change
+     * what they need, thus reducing code duplication. */
+    remap_props->lDevMin = remap_props->lMin = This->props[obj].lMin;
+    remap_props->lDevMax = remap_props->lMax = This->props[obj].lMax;
+
+    remap_props->lDeadZone = This->props[obj].lDeadZone;
+    remap_props->lSaturation = This->props[obj].lSaturation;
+}
+
+static void remap_apply(JoystickGenericImpl *This, int obj, ObjProps *remap_props)
+{
+    /* Many games poll the joystick immediately after setting the range
+     * for calibration purposes, so the old values need to be remapped
+     * to the new range before it does so */
+    switch (This->base.data_format.wine_df->rgodf[obj].dwOfs){
+    case DIJOFS_X        : This->js.lX  = joystick_map_axis(remap_props, This->js.lX); break;
+    case DIJOFS_Y        : This->js.lY  = joystick_map_axis(remap_props, This->js.lY); break;
+    case DIJOFS_Z        : This->js.lZ  = joystick_map_axis(remap_props, This->js.lZ); break;
+    case DIJOFS_RX       : This->js.lRx = joystick_map_axis(remap_props, This->js.lRx); break;
+    case DIJOFS_RY       : This->js.lRy = joystick_map_axis(remap_props, This->js.lRy); break;
+    case DIJOFS_RZ       : This->js.lRz = joystick_map_axis(remap_props, This->js.lRz); break;
+    case DIJOFS_SLIDER(0): This->js.rglSlider[0] = joystick_map_axis(remap_props, This->js.rglSlider[0]); break;
+    case DIJOFS_SLIDER(1): This->js.rglSlider[1] = joystick_map_axis(remap_props, This->js.rglSlider[1]); break;
+    default: break;
+    }
+}
+
+static void remap_range(JoystickGenericImpl *This, int obj, LPCDIPROPRANGE pr)
+{
+    ObjProps remap_props;
+    remap_init(This, obj, &remap_props);
+
+    remap_props.lMin = pr->lMin;
+    remap_props.lMax = pr->lMax;
+
+    remap_apply(This, obj, &remap_props);
+
+    /* Store new values */
+    This->props[obj].lMin = pr->lMin;
+    This->props[obj].lMax = pr->lMax;
+}
+
+static void remap_deadzone(JoystickGenericImpl *This, int obj, LPCDIPROPDWORD pd)
+{
+    ObjProps remap_props;
+    remap_init(This, obj, &remap_props);
+
+    remap_props.lDeadZone = pd->dwData;
+
+    remap_apply(This, obj, &remap_props);
+
+    /* Store new value */
+    This->props[obj].lDeadZone = pd->dwData;
+}
+
+static void remap_saturation(JoystickGenericImpl *This, int obj, LPCDIPROPDWORD pd)
+{
+    ObjProps remap_props;
+    remap_init(This, obj, &remap_props);
+
+    remap_props.lSaturation = pd->dwData;
+
+    remap_apply(This, obj, &remap_props);
+
+    /* Store new value */
+    This->props[obj].lSaturation = pd->dwData;
+}
+
 /******************************************************************************
   *     SetProperty : change input device properties
   */
@@ -278,7 +399,6 @@ HRESULT WINAPI JoystickWGenericImpl_SetProperty(LPDIRECTINPUTDEVICE8W iface, REF
 {
     JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
     DWORD i;
-    ObjProps remap_props;
 
     TRACE("(%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
 
@@ -295,69 +415,15 @@ HRESULT WINAPI JoystickWGenericImpl_SetProperty(LPDIRECTINPUTDEVICE8W iface, REF
         case (DWORD_PTR)DIPROP_RANGE: {
             LPCDIPROPRANGE pr = (LPCDIPROPRANGE)ph;
             if (ph->dwHow == DIPH_DEVICE) {
-
-                /* Many games poll the joystick immediately after setting the range
-                 * for calibration purposes, so the old values need to be remapped
-                 * to the new range before it does so */
-
                 TRACE("proprange(%d,%d) all\n", pr->lMin, pr->lMax);
-                for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++) {
-
-                    remap_props.lDevMin = This->props[i].lMin;
-                    remap_props.lDevMax = This->props[i].lMax;
-
-                    remap_props.lDeadZone = This->props[i].lDeadZone;
-                    remap_props.lSaturation = This->props[i].lSaturation;
-
-                    remap_props.lMin = pr->lMin;
-                    remap_props.lMax = pr->lMax;
-
-                    switch (This->base.data_format.wine_df->rgodf[i].dwOfs) {
-                    case DIJOFS_X        : This->js.lX  = joystick_map_axis(&remap_props, This->js.lX); break;
-                    case DIJOFS_Y        : This->js.lY  = joystick_map_axis(&remap_props, This->js.lY); break;
-                    case DIJOFS_Z        : This->js.lZ  = joystick_map_axis(&remap_props, This->js.lZ); break;
-                    case DIJOFS_RX       : This->js.lRx = joystick_map_axis(&remap_props, This->js.lRx); break;
-                    case DIJOFS_RY       : This->js.lRy = joystick_map_axis(&remap_props, This->js.lRy); break;
-                    case DIJOFS_RZ       : This->js.lRz = joystick_map_axis(&remap_props, This->js.lRz); break;
-                    case DIJOFS_SLIDER(0): This->js.rglSlider[0] = joystick_map_axis(&remap_props, This->js.rglSlider[0]); break;
-                    case DIJOFS_SLIDER(1): This->js.rglSlider[1] = joystick_map_axis(&remap_props, This->js.rglSlider[1]); break;
-                   default: break;
-                    }
-
-                    This->props[i].lMin = pr->lMin;
-                    This->props[i].lMax = pr->lMax;
-                }
+                for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++)
+                    remap_range(This, i, pr);
             } else {
                 int obj = find_property(&This->base.data_format, ph);
 
                 TRACE("proprange(%d,%d) obj=%d\n", pr->lMin, pr->lMax, obj);
-                if (obj >= 0) {
-
-                    remap_props.lDevMin = This->props[obj].lMin;
-                    remap_props.lDevMax = This->props[obj].lMax;
-
-                    remap_props.lDeadZone = This->props[obj].lDeadZone;
-                    remap_props.lSaturation = This->props[obj].lSaturation;
-
-                    remap_props.lMin = pr->lMin;
-                    remap_props.lMax = pr->lMax;
-
-                    switch (This->base.data_format.wine_df->rgodf[obj].dwOfs) {
-                    case DIJOFS_X        : This->js.lX  = joystick_map_axis(&remap_props, This->js.lX); break;
-                    case DIJOFS_Y        : This->js.lY  = joystick_map_axis(&remap_props, This->js.lY); break;
-                    case DIJOFS_Z        : This->js.lZ  = joystick_map_axis(&remap_props, This->js.lZ); break;
-                    case DIJOFS_RX       : This->js.lRx = joystick_map_axis(&remap_props, This->js.lRx); break;
-                    case DIJOFS_RY       : This->js.lRy = joystick_map_axis(&remap_props, This->js.lRy); break;
-                    case DIJOFS_RZ       : This->js.lRz = joystick_map_axis(&remap_props, This->js.lRz); break;
-                    case DIJOFS_SLIDER(0): This->js.rglSlider[0] = joystick_map_axis(&remap_props, This->js.rglSlider[0]); break;
-                    case DIJOFS_SLIDER(1): This->js.rglSlider[1] = joystick_map_axis(&remap_props, This->js.rglSlider[1]); break;
-                   default: break;
-                    }
-
-                    This->props[obj].lMin = pr->lMin;
-                    This->props[obj].lMax = pr->lMax;
-                    return DI_OK;
-                }
+                if (obj >= 0)
+                    remap_range(This, obj, pr);
             }
             break;
         }
@@ -366,15 +432,13 @@ HRESULT WINAPI JoystickWGenericImpl_SetProperty(LPDIRECTINPUTDEVICE8W iface, REF
             if (ph->dwHow == DIPH_DEVICE) {
                 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;
+                    remap_deadzone(This, i, pd);
             } else {
                 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;
-                }
+                if (obj >= 0)
+                    remap_deadzone(This, obj, pd);
             }
             break;
         }
@@ -383,18 +447,21 @@ HRESULT WINAPI JoystickWGenericImpl_SetProperty(LPDIRECTINPUTDEVICE8W iface, REF
             if (ph->dwHow == DIPH_DEVICE) {
                 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;
+                    remap_saturation(This, i, pd);
             } else {
                 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;
-                }
+                if (obj >= 0)
+                    remap_saturation(This, obj, pd);
             }
             break;
         }
+        case (DWORD_PTR)DIPROP_CALIBRATIONMODE: {
+          LPCDIPROPDWORD       pd = (LPCDIPROPDWORD)ph;
+          FIXME("DIPROP_CALIBRATIONMODE(%d)\n", pd->dwData);
+          break;
+        }
         default:
             return IDirectInputDevice2WImpl_SetProperty(iface, rguid, ph);
         }
@@ -461,7 +528,7 @@ HRESULT WINAPI JoystickWGenericImpl_GetCapabilities(LPDIRECTINPUTDEVICE8W iface,
     JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
     int size;
 
-    TRACE("%p->(%p)\n",iface,lpDIDevCaps);
+    TRACE("%p->(%p)\n",This,lpDIDevCaps);
 
     if (lpDIDevCaps == NULL) {
         WARN("invalid pointer\n");
@@ -544,7 +611,7 @@ HRESULT WINAPI JoystickWGenericImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REF
 {
     JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
 
-    TRACE("(%p,%s,%p)\n", iface, debugstr_guid(rguid), pdiph);
+    TRACE("(%p,%s,%p)\n", This, debugstr_guid(rguid), pdiph);
 
     if (TRACE_ON(dinput))
         _dump_DIPROPHEADER(pdiph);
@@ -627,7 +694,7 @@ HRESULT WINAPI JoystickAGenericImpl_GetDeviceInfo(
     DIPROPDWORD pd;
     DWORD index = 0;
 
-    TRACE("(%p,%p)\n", iface, pdidi);
+    TRACE("(%p,%p)\n", This, pdidi);
 
     if (pdidi == NULL) {
         WARN("invalid pointer\n");
@@ -654,7 +721,7 @@ HRESULT WINAPI JoystickAGenericImpl_GetDeviceInfo(
     /* we only support traditional joysticks for now */
     pdidi->dwDevType = This->devcaps.dwDevType;
     snprintf(pdidi->tszInstanceName, MAX_PATH, "Joystick %d", index);
-    strcpy(pdidi->tszProductName, This->name);
+    lstrcpynA(pdidi->tszProductName, This->name, MAX_PATH);
     if (pdidi->dwSize > sizeof(DIDEVICEINSTANCE_DX3A)) {
         pdidi->guidFFDriver = GUID_NULL;
         pdidi->wUsagePage = 0;
@@ -771,8 +838,31 @@ HRESULT WINAPI JoystickWGenericImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W iface,
     JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
     unsigned int i, j;
     BOOL has_actions = FALSE;
+    WCHAR *username;
+    DWORD size;
+    BOOL load_success = FALSE;
+
+    FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", This, lpdiaf, debugstr_w(lpszUserName), dwFlags);
 
-    FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
+    /* Unless asked the contrary by these flags, try to load a previous mapping */
+    if (!(dwFlags & DIDBAM_HWDEFAULTS))
+    {
+        if (!lpszUserName)
+            GetUserNameW(NULL, &size);
+        else
+            size = lstrlenW(lpszUserName) + 1;
+
+        username = heap_alloc(size * sizeof(WCHAR));
+        if (!lpszUserName)
+            GetUserNameW(username, &size);
+        else
+            lstrcpynW(username, lpszUserName, size);
+
+        load_success = load_mapping_settings(&This->base, lpdiaf, username);
+        heap_free(username);
+    }
+
+    if (load_success) return DI_OK;
 
     for (i=0; i < lpdiaf->dwNumActions; i++)
     {
@@ -850,7 +940,7 @@ HRESULT WINAPI JoystickWGenericImpl_SetActionMap(LPDIRECTINPUTDEVICE8W iface,
 {
     JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
 
-    FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
+    FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", This, lpdiaf, debugstr_w(lpszUserName), dwFlags);
 
     return _set_action_map(iface, lpdiaf, lpszUserName, dwFlags, This->base.data_format.wine_df);
 }
@@ -878,6 +968,8 @@ HRESULT WINAPI JoystickAGenericImpl_SetActionMap(LPDIRECTINPUTDEVICE8A iface,
 
     hr = JoystickWGenericImpl_SetActionMap(&This->base.IDirectInputDevice8W_iface, &diafW, lpszUserNameW, dwFlags);
 
+    lpdiaf->dwCRC = diafW.dwCRC;
+
     HeapFree(GetProcessHeap(), 0, diafW.rgoAction);
     HeapFree(GetProcessHeap(), 0, lpszUserNameW);
 
@@ -950,6 +1042,7 @@ HRESULT setup_dinput_options(JoystickGenericImpl *This, const int *default_axis_
     int tokens = 0;
     int axis = 0;
     int pov = 0;
+    int button;
 
     get_app_key(&hkey, &appkey);
 
@@ -961,6 +1054,34 @@ HRESULT setup_dinput_options(JoystickGenericImpl *This, const int *default_axis_
         TRACE("setting default deadzone to: \"%s\" %d\n", buffer, This->deadzone);
     }
 
+    for (button = 0; button < MAX_MAP_BUTTONS; button++)
+        This->button_map[button] = button;
+
+    if (!get_config_key(hkey, appkey, "ButtonMap", buffer, sizeof(buffer)))
+    {
+        static const char *delim = ",";
+        int button = 0;
+        char *token;
+
+        TRACE("ButtonMap = \"%s\"\n", buffer);
+        for (token = strtok(buffer, delim);
+             token != NULL && button < MAX_MAP_BUTTONS;
+             token = strtok(NULL, delim), button++)
+        {
+            char *s;
+            int value = strtol(token, &s, 10);
+            if (value < 0 || *s != '\0')
+            {
+                ERR("invalid button number: \"%s\"", token);
+            }
+            else
+            {
+                TRACE("mapping physical button %d to DInput button %d", value, button);
+                This->button_map[value] = button;
+            }
+        }
+    }
+
     This->axis_map = HeapAlloc(GetProcessHeap(), 0, This->device_axis_count * sizeof(int));
     if (!This->axis_map) return DIERR_OUTOFMEMORY;
 
index 963e62f..3ecbf44 100644 (file)
@@ -59,6 +59,7 @@
 #include "windef.h"
 #include "winbase.h"
 #include "winerror.h"
+#include "devguid.h"
 #include "dinput.h"
 
 #include "dinput_private.h"
@@ -84,6 +85,8 @@ struct JoyDev
     int  *dev_axes_map;
 
     WORD vendor_id, product_id, bus_type;
+
+    BOOL is_joystick;
 };
 
 typedef struct JoystickImpl JoystickImpl;
@@ -123,19 +126,6 @@ static const GUID DInput_Wine_Joystick_GUID = { /* 9e573ed9-7734-11d2-8d4a-23903
   {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
 };
 
-/*
- * Construct the GUID in the same way of Windows doing this.
- * Data1 is concatenation of productid and vendorid.
- * Data2 and Data3 are NULL.
- * Data4 seems to be a constant.
- */
-static const GUID DInput_Wine_Joystick_Constant_Part_GUID = {
-  0x000000000,
-  0x0000,
-  0x0000,
-  {0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44}
-};
-
 #define MAX_JOYSTICKS 64
 static INT joystick_devices_count = -1;
 static struct JoyDev *joystick_devices;
@@ -177,6 +167,8 @@ static INT find_joystick_devices(void)
         int fd;
         struct JoyDev joydev, *new_joydevs;
         BYTE axes_map[ABS_MAX + 1];
+        SHORT btn_map[KEY_MAX - BTN_MISC + 1];
+        BOOL is_stylus = FALSE;
 
         snprintf(joydev.device, sizeof(joydev.device), "%s%d", JOYDEV_NEW, i);
         if ((fd = open(joydev.device, O_RDONLY)) == -1)
@@ -220,6 +212,52 @@ static INT find_joystick_devices(void)
         joydev.button_count = 2;
 #endif
 
+        joydev.is_joystick = FALSE;
+        if (ioctl(fd, JSIOCGBTNMAP, btn_map) < 0)
+        {
+            WARN("ioctl(%s,JSIOCGBTNMAP) failed: %s\n", joydev.device, strerror(errno));
+        }
+        else
+        {
+            INT j;
+            /* in lieu of properly reporting HID usage, detect presence of
+             * "joystick buttons" and report those devices as joysticks instead of
+             * gamepads */
+            for (j = 0; !joydev.is_joystick && j < joydev.button_count; j++)
+            {
+                switch (btn_map[j])
+                {
+                case BTN_TRIGGER:
+                case BTN_THUMB:
+                case BTN_THUMB2:
+                case BTN_TOP:
+                case BTN_TOP2:
+                case BTN_PINKIE:
+                case BTN_BASE:
+                case BTN_BASE2:
+                case BTN_BASE3:
+                case BTN_BASE4:
+                case BTN_BASE5:
+                case BTN_BASE6:
+                case BTN_DEAD:
+                    joydev.is_joystick = TRUE;
+                    break;
+                case BTN_STYLUS:
+                    is_stylus = TRUE;
+                    break;
+                default:
+                    break;
+                }
+            }
+        }
+
+        if(is_stylus)
+        {
+            TRACE("Stylus detected. Skipping\n");
+            close(fd);
+            continue;
+        }
+
         if (ioctl(fd, JSIOCGAXMAP, axes_map) < 0)
         {
             WARN("ioctl(%s,JSIOCGAXMAP) failed: %s\n", joydev.device, strerror(errno));
@@ -239,6 +277,14 @@ static INT find_joystick_devices(void)
                         joydev.dev_axes_map[j] = j;
                         found_axes++;
                     }
+                    else if (axes_map[j] <= 10)
+                    {
+                        /* Axes 8 through 10 are Wheel, Gas and Brake,
+                         * remap to 0, 1 and 2
+                         */
+                        joydev.dev_axes_map[j] = axes_map[j] - 8;
+                        found_axes++;
+                    }
                     else if (axes_map[j] == 16 ||
                              axes_map[j] == 17)
                     {
@@ -279,7 +325,7 @@ static INT find_joystick_devices(void)
         else
         {
             /* Concatenate product_id with vendor_id to mimic Windows behaviour */
-            joydev.guid_product       = DInput_Wine_Joystick_Constant_Part_GUID;
+            joydev.guid_product       = DInput_PIDVID_Product_GUID;
             joydev.guid_product.Data1 = MAKELONG(joydev.vendor_id, joydev.product_id);
         }
 
@@ -314,11 +360,7 @@ static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD ver
     lpddi->guidInstance = DInput_Wine_Joystick_GUID;
     lpddi->guidInstance.Data3 = id;
     lpddi->guidProduct = joystick_devices[id].guid_product;
-    /* we only support traditional joysticks for now */
-    if (version >= 0x0800)
-        lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
-    else
-        lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
+    lpddi->dwDevType = get_device_type(version, joystick_devices[id].is_joystick);
 
     /* Assume the joystick as HID if it is attached to USB bus and has a valid VID/PID */
     if (joystick_devices[id].bus_type == BUS_USB &&
@@ -326,7 +368,7 @@ static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD ver
     {
         lpddi->dwDevType |= DIDEVTYPE_HID;
         lpddi->wUsagePage = 0x01; /* Desktop */
-        if (lpddi->dwDevType == DI8DEVTYPE_JOYSTICK || lpddi->dwDevType == DIDEVTYPE_JOYSTICK)
+        if (joystick_devices[id].is_joystick)
             lpddi->wUsage = 0x04; /* Joystick */
         else
             lpddi->wUsage = 0x05; /* Game Pad */
@@ -372,7 +414,7 @@ static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINS
     }
 
     if ((dwDevType == 0) ||
-       ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
+       ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
        (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
         /* check whether we have a joystick */
         if ((fd = open(joystick_devices[id].device, O_RDONLY)) == -1)
@@ -401,7 +443,7 @@ static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINS
     }
 
     if ((dwDevType == 0) ||
-       ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
+       ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
        (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
         /* check whether we have a joystick */
         if ((fd = open(joystick_devices[id].device, O_RDONLY)) == -1)
@@ -673,7 +715,7 @@ static HRESULT WINAPI JoystickLinuxWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface
 {
     JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
 
-    TRACE("(this=%p,%s,%p)\n", iface, debugstr_guid(rguid), pdiph);
+    TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(rguid), pdiph);
     _dump_DIPROPHEADER(pdiph);
 
     if (!IS_DIPROP(rguid)) return DI_OK;
@@ -699,6 +741,29 @@ static HRESULT WINAPI JoystickLinuxWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface
             break;
         }
 
+        case (DWORD_PTR) DIPROP_GUIDANDPATH:
+        {
+            static const WCHAR formatW[] = {'\\','\\','?','\\','h','i','d','#','v','i','d','_','%','0','4','x','&',
+                                            'p','i','d','_','%','0','4','x','&','%','s','_','%','h','u',0};
+            static const WCHAR miW[] = {'m','i',0};
+            static const WCHAR igW[] = {'i','g',0};
+
+            BOOL is_gamepad;
+            LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph;
+            WORD vid = This->joydev->vendor_id;
+            WORD pid = This->joydev->product_id;
+
+            if (!pid || !vid)
+                return DIERR_UNSUPPORTED;
+
+            is_gamepad = is_xinput_device(&This->generic.devcaps, vid, pid);
+            pd->guidClass = GUID_DEVCLASS_HIDCLASS;
+            sprintfW(pd->wszPath, formatW, vid, pid, is_gamepad ? igW : miW, get_joystick_index(&This->generic.base.guid));
+
+            TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
+            break;
+        }
+
     default:
         return JoystickWGenericImpl_GetProperty(iface, rguid, pdiph);
     }
@@ -807,10 +872,13 @@ static void joy_polldev(LPDIRECTINPUTDEVICE8A iface)
               jse.type,jse.number,jse.value);
         if (jse.type & JS_EVENT_BUTTON)
         {
+            int button;
             if (jse.number >= This->generic.devcaps.dwButtons) return;
 
-            inst_id = DIDFT_MAKEINSTANCE(jse.number) | DIDFT_PSHBUTTON;
-            This->generic.js.rgbButtons[jse.number] = value = jse.value ? 0x80 : 0x00;
+            button = This->generic.button_map[jse.number];
+
+            inst_id = DIDFT_MAKEINSTANCE(button) | DIDFT_PSHBUTTON;
+            This->generic.js.rgbButtons[button] = value = jse.value ? 0x80 : 0x00;
         }
         else if (jse.type & JS_EVENT_AXIS)
         {
index 910e755..8865532 100644 (file)
@@ -57,6 +57,7 @@
 #include "winbase.h"
 #include "winerror.h"
 #include "winreg.h"
+#include "devguid.h"
 #include "dinput.h"
 
 #include "dinput_private.h"
@@ -83,6 +84,13 @@ struct wine_input_absinfo {
     LONG flat;
 };
 
+enum wine_joystick_linuxinput_fd_state {
+    WINE_FD_STATE_CLOSED = 0,    /* No device has been opened yet */
+    WINE_FD_STATE_OK,            /* File descriptor is open and ready for reading */
+    WINE_FD_STATE_DISCONNECTED,  /* Read error occurred; might be able to reopen later */
+    WINE_FD_STATE_INVALID,       /* Device is no longer available at original pathname */
+};
+
 /* implemented in effect_linuxinput.c */
 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);
@@ -100,7 +108,7 @@ struct JoyDev {
        GUID guid;
        GUID guid_product;
 
-        BOOL has_ff;
+        BOOL has_ff, is_joystick;
         int num_effects;
 
        /* data returned by EVIOCGBIT for caps, EV_ABS, EV_KEY, and EV_FF */
@@ -122,6 +130,7 @@ struct JoystickImpl
 
        /* joystick private */
        int                             joyfd;
+       enum wine_joystick_linuxinput_fd_state joyfd_state;
 
        int                             dev_axes_to_di[ABS_MAX];
         POINTL                          povs[4];
@@ -164,19 +173,6 @@ static const GUID DInput_Wine_Joystick_Base_GUID = { /* 9e573eda-7734-11d2-8d4a-
   {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
 };
 
-/*
- * Construct the GUID in the same way of Windows doing this.
- * Data1 is concatenation of productid and vendorid.
- * Data2 and Data3 are NULL.
- * Data4 seems to be a constant.
- */
-static const GUID DInput_Wine_Joystick_Constant_Part_GUID = {
-  0x000000000,
-  0x0000,
-  0x0000,
-  {0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44}
-};
-
 #define test_bit(arr,bit) (((BYTE*)(arr))[(bit)>>3]&(1<<((bit)&7)))
 
 #define MAX_JOYDEV 64
@@ -211,10 +207,7 @@ static void find_joydevs(void)
         }
 
         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)
         {
@@ -237,15 +230,41 @@ static void find_joydevs(void)
 
         /* 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) ||
+        if (((!test_bit(joydev.absbits, ABS_X) || !test_bit(joydev.absbits, ABS_Y)) &&
+             !test_bit(joydev.absbits, ABS_WHEEL) &&
+             !test_bit(joydev.absbits, ABS_GAS) &&
+             !test_bit(joydev.absbits, ABS_BRAKE)) ||
             !(test_bit(joydev.keybits, BTN_TRIGGER) ||
               test_bit(joydev.keybits, BTN_A) ||
-              test_bit(joydev.keybits, BTN_1)))
+              test_bit(joydev.keybits, BTN_1) ||
+              test_bit(joydev.keybits, BTN_BASE) ||
+              test_bit(joydev.keybits, BTN_GEAR_UP) ||
+              test_bit(joydev.keybits, BTN_GEAR_DOWN)))
         {
             close(fd);
             continue;
         }
 
+        /* in lieu of properly reporting HID usage, detect presence of
+         * "joystick buttons" and report those devices as joysticks instead of
+         * gamepads */
+        joydev.is_joystick =
+            test_bit(joydev.keybits, BTN_TRIGGER) ||
+            test_bit(joydev.keybits, BTN_THUMB) ||
+            test_bit(joydev.keybits, BTN_THUMB2) ||
+            test_bit(joydev.keybits, BTN_TOP) ||
+            test_bit(joydev.keybits, BTN_TOP2) ||
+            test_bit(joydev.keybits, BTN_PINKIE) ||
+            test_bit(joydev.keybits, BTN_BASE) ||
+            test_bit(joydev.keybits, BTN_BASE2) ||
+            test_bit(joydev.keybits, BTN_BASE3) ||
+            test_bit(joydev.keybits, BTN_BASE4) ||
+            test_bit(joydev.keybits, BTN_BASE5) ||
+            test_bit(joydev.keybits, BTN_BASE6) ||
+            test_bit(joydev.keybits, BTN_GEAR_UP) ||
+            test_bit(joydev.keybits, BTN_GEAR_DOWN) ||
+            test_bit(joydev.keybits, BTN_DEAD);
+
         if (!(joydev.device = HeapAlloc(GetProcessHeap(), 0, strlen(buf) + 1)))
         {
             close(fd);
@@ -320,7 +339,7 @@ static void find_joydevs(void)
             joydev.bus_type = device_id.bustype;
 
             /* Concatenate product_id with vendor_id to mimic Windows behaviour */
-            joydev.guid_product       = DInput_Wine_Joystick_Constant_Part_GUID;
+            joydev.guid_product       = DInput_PIDVID_Product_GUID;
             joydev.guid_product.Data1 = MAKELONG(joydev.vendor_id, joydev.product_id);
         }
 
@@ -353,11 +372,7 @@ static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD ver
     lpddi->guidInstance = joydevs[id].guid;
     lpddi->guidProduct  = joydevs[id].guid_product;
     lpddi->guidFFDriver = GUID_NULL;
-
-    if (version >= 0x0800)
-        lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
-    else
-        lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
+    lpddi->dwDevType = get_device_type(version, joydevs[id].is_joystick);
 
     /* Assume the joystick as HID if it is attached to USB bus and has a valid VID/PID */
     if (joydevs[id].bus_type == BUS_USB &&
@@ -365,7 +380,7 @@ static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD ver
     {
         lpddi->dwDevType |= DIDEVTYPE_HID;
         lpddi->wUsagePage = 0x01; /* Desktop */
-        if (lpddi->dwDevType == DI8DEVTYPE_JOYSTICK || lpddi->dwDevType == DIDEVTYPE_JOYSTICK)
+        if (joydevs[id].is_joystick)
             lpddi->wUsage = 0x04; /* Joystick */
         else
             lpddi->wUsage = 0x05; /* Game Pad */
@@ -391,8 +406,8 @@ static void fill_joystick_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD ver
     lpddi->guidInstance = lpddiW.guidInstance;
     lpddi->guidProduct = lpddiW.guidProduct;
     lpddi->dwDevType = lpddiW.dwDevType;
-    strcpy(lpddi->tszInstanceName, joydevs[id].name);
-    strcpy(lpddi->tszProductName,  joydevs[id].name);
+    lstrcpynA(lpddi->tszInstanceName, joydevs[id].name, MAX_PATH);
+    lstrcpynA(lpddi->tszProductName,  joydevs[id].name, MAX_PATH);
     lpddi->guidFFDriver = lpddiW.guidFFDriver;
     lpddi->wUsagePage = lpddiW.wUsagePage;
     lpddi->wUsage = lpddiW.wUsage;
@@ -407,7 +422,7 @@ static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINS
   }
 
   if (!((dwDevType == 0) ||
-        ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
+        ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
     return S_FALSE;
 
@@ -432,7 +447,7 @@ static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINS
   }
 
   if (!((dwDevType == 0) ||
-        ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
+        ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
     return S_FALSE;
 
@@ -466,6 +481,7 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
     newDevice->generic.base.dinput = dinput;
     newDevice->generic.joy_polldev = joy_polldev;
     newDevice->joyfd       = -1;
+    newDevice->joyfd_state = WINE_FD_STATE_CLOSED;
     newDevice->joydev      = &joydevs[index];
     newDevice->generic.name        = newDevice->joydev->name;
     list_init(&newDevice->ff_effects);
@@ -483,14 +499,18 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig
     /* Count number of available axes - supported Axis & POVs */
     for (i = 0; i < ABS_MAX; i++)
     {
-        if (i < WINE_JOYSTICK_MAX_AXES &&
+        if (idx < WINE_JOYSTICK_MAX_AXES &&
+            i < ABS_HAT0X &&
             test_bit(newDevice->joydev->absbits, i))
         {
             newDevice->generic.device_axis_count++;
             newDevice->dev_axes_to_di[i] = idx;
             newDevice->generic.props[idx].lDevMin = newDevice->joydev->axes[i].minimum;
             newDevice->generic.props[idx].lDevMax = newDevice->joydev->axes[i].maximum;
-            default_axis_map[idx] = i;
+            if (i >= 8 && i <= 10) /* If it's a wheel axis... */
+                default_axis_map[idx] = i - 8; /* ... remap to X/Y/Z */
+            else
+                default_axis_map[idx] = i;
             idx++;
         }
         else
@@ -669,38 +689,15 @@ static HRESULT joydev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REF
     return DIERR_DEVICENOTREG;
 }
 
-
-const struct dinput_device joystick_linuxinput_device = {
-  "Wine Linux-input joystick driver",
-  joydev_enum_deviceA,
-  joydev_enum_deviceW,
-  joydev_create_device
-};
-
-/******************************************************************************
-  *     Acquire : gets exclusive control of the joystick
-  */
-static HRESULT WINAPI JoystickWImpl_Acquire(LPDIRECTINPUTDEVICE8W iface)
+static int joydev_open_evdev(JoystickImpl *This)
 {
-    JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
-    HRESULT res;
+    int fd;
 
-    TRACE("(this=%p)\n",This);
-
-    if ((res = IDirectInputDevice2WImpl_Acquire(iface)) != DI_OK)
-    {
-        WARN("Failed to acquire: %x\n", res);
-        return res;
-    }
-
-    if ((This->joyfd = open(This->joydev->device, O_RDWR)) == -1)
+    if ((fd = open(This->joydev->device, O_RDWR)) == -1)
     {
-        if ((This->joyfd = open(This->joydev->device, O_RDONLY)) == -1)
+        if ((fd = 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));
-            IDirectInputDevice2WImpl_Unacquire(iface);
-            return DIERR_NOTFOUND;
         }
         else
         {
@@ -715,18 +712,53 @@ static HRESULT WINAPI JoystickWImpl_Acquire(LPDIRECTINPUTDEVICE8W iface)
         event.type = EV_FF;
         event.code = FF_GAIN;
         event.value = This->ff_gain;
-        if (write(This->joyfd, &event, sizeof(event)) == -1)
+        if (write(fd, &event, sizeof(event)) == -1)
             ERR("Failed to set gain (%i): %d %s\n", This->ff_gain, errno, strerror(errno));
         if (!This->ff_autocenter)
         {
             /* Disable autocenter. */
             event.code = FF_AUTOCENTER;
             event.value = 0;
-            if (write(This->joyfd, &event, sizeof(event)) == -1)
+            if (write(fd, &event, sizeof(event)) == -1)
                 ERR("Failed disabling autocenter: %d %s\n", errno, strerror(errno));
         }
     }
 
+    return fd;
+}
+
+
+const struct dinput_device joystick_linuxinput_device = {
+  "Wine Linux-input joystick driver",
+  joydev_enum_deviceA,
+  joydev_enum_deviceW,
+  joydev_create_device
+};
+
+/******************************************************************************
+  *     Acquire : gets exclusive control of the joystick
+  */
+static HRESULT WINAPI JoystickWImpl_Acquire(LPDIRECTINPUTDEVICE8W iface)
+{
+    JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
+    HRESULT res;
+
+    TRACE("(this=%p)\n",This);
+
+    if ((res = IDirectInputDevice2WImpl_Acquire(iface)) != DI_OK)
+    {
+        WARN("Failed to acquire: %x\n", res);
+        return res;
+    }
+
+    if ((This->joyfd = joydev_open_evdev(This)) == -1)
+    {
+        ERR("Failed to open device %s: %d %s\n", This->joydev->device, errno, strerror(errno));
+        IDirectInputDevice2WImpl_Unacquire(iface);
+        return DIERR_NOTFOUND;
+    }
+
+    This->joyfd_state = WINE_FD_STATE_OK;
     return DI_OK;
 }
 
@@ -764,6 +796,7 @@ static HRESULT WINAPI JoystickWImpl_Unacquire(LPDIRECTINPUTDEVICE8W iface)
 
       close(This->joyfd);
       This->joyfd = -1;
+      This->joyfd_state = WINE_FD_STATE_CLOSED;
     }
     return res;
 }
@@ -808,23 +841,79 @@ static void joy_polldev(LPDIRECTINPUTDEVICE8A iface)
     struct input_event ie;
     JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
 
-    if (This->joyfd==-1)
-       return;
+    if (This->joyfd == -1)
+    {
+        int fd;
+        char namebuf[MAX_PATH + 8];  /* 8 == strlen(EVDEVDRIVER) */
+
+        if (This->joyfd_state != WINE_FD_STATE_DISCONNECTED)
+            return;
+        /* Try to reconnect to the device. */
+        fd = joydev_open_evdev(This);
+        if (fd == -1)
+            return;
+        namebuf[sizeof(namebuf) - strlen(EVDEVDRIVER) - 1] = 0;
+        if (ioctl(fd, EVIOCGNAME(sizeof(namebuf) - strlen(EVDEVDRIVER) - 1), namebuf) == -1)
+        {
+            /* Couldn't get the name; assume it's a different device. */
+            ERR("EVIOCGNAME(%s) failed: %d %s", This->joydev->device, errno, strerror(errno));
+            This->joyfd_state = WINE_FD_STATE_INVALID;
+            return;
+        }
+        strcat(namebuf, EVDEVDRIVER);  /* Guaranteed to be safe. */
+        if (strcmp(namebuf, This->joydev->name) != 0)
+        {
+            ERR("Device %s changed from \"%s\" to \"%s\"!  Can't reconnect.\n", This->joydev->device, This->joydev->name, namebuf);
+            This->joyfd_state = WINE_FD_STATE_INVALID;
+            return;
+        }
+        if (InterlockedCompareExchange(&This->joyfd, fd, -1) == -1)
+        {
+            This->joyfd_state = WINE_FD_STATE_OK;
+            TRACE("Reconnected to \"%s\" on %s", This->joydev->name, This->joydev->device);
+        }
+        else
+        {
+            /* Somebody beat us to it!  Throw away our fd and use theirs. */
+            close(fd);
+        }
+    }
 
     while (1)
     {
         LONG value = 0;
         int inst_id = -1;
+        int result;
 
        plfd.fd = This->joyfd;
        plfd.events = POLLIN;
 
-       if (poll(&plfd,1,0) != 1)
-           return;
+        result = poll(&plfd,1,0);
+        if (result != 1)
+        {
+            if (result == -1)
+            {
+                ERR("poll failed: %d %s\n", errno, strerror(errno));
+                close(This->joyfd);
+                This->joyfd = -1;
+                This->joyfd_state = WINE_FD_STATE_DISCONNECTED;
+            }
+            return;
+        }
 
        /* we have one event, so we can read */
-       if (sizeof(ie)!=read(This->joyfd,&ie,sizeof(ie)))
-           return;
+        result = read(This->joyfd,&ie,sizeof(ie));
+        if (result != sizeof(ie))
+        {
+            if (result == -1)
+            {
+                ERR("read failed: %d %s\n", errno, strerror(errno));
+                close(This->joyfd);
+                This->joyfd = -1;
+                This->joyfd_state = WINE_FD_STATE_DISCONNECTED;
+            }
+            return;
+        }
 
        TRACE("input_event: type %d, code %d, value %d\n",ie.type,ie.code,ie.value);
        switch (ie.type) {
@@ -836,6 +925,8 @@ static void joy_polldev(LPDIRECTINPUTDEVICE8A iface)
             if (btn & 0x80)
             {
                 btn &= 0x7F;
+                btn = This->generic.button_map[btn];
+
                 inst_id = DIDFT_MAKEINSTANCE(btn) | DIDFT_PSHBUTTON;
                 This->generic.js.rgbButtons[btn] = value = ie.value ? 0x80 : 0x00;
             }
@@ -923,11 +1014,6 @@ static HRESULT WINAPI JoystickWImpl_SetProperty(LPDIRECTINPUTDEVICE8W iface, REF
 
   if (IS_DIPROP(rguid)) {
     switch (LOWORD(rguid)) {
-    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;
 
@@ -973,7 +1059,7 @@ static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REF
 {
     JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
 
-    TRACE("(this=%p,%s,%p)\n", iface, debugstr_guid(rguid), pdiph);
+    TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(rguid), pdiph);
     _dump_DIPROPHEADER(pdiph);
 
     if (!IS_DIPROP(rguid)) return DI_OK;
@@ -1016,6 +1102,29 @@ static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REF
         break;
     }
 
+    case (DWORD_PTR) DIPROP_GUIDANDPATH:
+    {
+        static const WCHAR formatW[] = {'\\','\\','?','\\','h','i','d','#','v','i','d','_','%','0','4','x','&',
+                                        'p','i','d','_','%','0','4','x','&','%','s','_','%','h','u',0};
+        static const WCHAR miW[] = {'m','i',0};
+        static const WCHAR igW[] = {'i','g',0};
+
+        BOOL is_gamepad;
+        LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph;
+        WORD vid = This->joydev->vendor_id;
+        WORD pid = This->joydev->product_id;
+
+        if (!pid || !vid)
+            return DIERR_UNSUPPORTED;
+
+        is_gamepad = is_xinput_device(&This->generic.devcaps, vid, pid);
+        pd->guidClass = GUID_DEVCLASS_HIDCLASS;
+        sprintfW(pd->wszPath, formatW, vid, pid, is_gamepad ? igW : miW, get_joystick_index(&This->generic.base.guid));
+
+        TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
+        break;
+    }
+
     default:
         return JoystickWGenericImpl_GetProperty(iface, rguid, pdiph);
     }
index 0701b9f..061423a 100644 (file)
@@ -85,6 +85,7 @@
 #include "winbase.h"
 #include "winerror.h"
 #include "winreg.h"
+#include "devguid.h"
 #include "dinput.h"
 
 #include "dinput_private.h"
@@ -124,6 +125,11 @@ static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W
            JoystickGenericImpl, base), JoystickImpl, generic);
 }
 
+static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl(JoystickImpl *This)
+{
+    return &This->generic.base.IDirectInputDevice8W_iface;
+}
+
 typedef struct _EffectImpl {
     IDirectInputEffect IDirectInputEffect_iface;
     LONG ref;
@@ -801,7 +807,11 @@ static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
                     TRACE("kIOHIDElementTypeInput_Button\n");
                     if(button_idx < 128)
                     {
-                        IOHIDDeviceGetValue(hid_device, element, &valueRef);
+                        valueRef = NULL;
+                        if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
+                            return;
+                        if (valueRef == NULL)
+                            return;
                         val = IOHIDValueGetIntegerValue(valueRef);
                         newVal = val ? 0x80 : 0x0;
                         oldVal = device->generic.js.rgbButtons[button_idx];
@@ -809,6 +819,8 @@ static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
                         TRACE("valueRef %s val %d oldVal %d newVal %d\n", debugstr_cf(valueRef), val, oldVal, newVal);
                         if (oldVal != newVal)
                         {
+                            button_idx = device->generic.button_map[button_idx];
+
                             inst_id = DIDFT_MAKEINSTANCE(button_idx) | DIDFT_PSHBUTTON;
                             queue_event(iface,inst_id,newVal,GetCurrentTime(),device->generic.base.dinput->evsequence++);
                         }
@@ -823,7 +835,11 @@ static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
                         case kHIDUsage_GD_Hatswitch:
                         {
                             TRACE("kIOHIDElementTypeInput_Misc / kHIDUsage_GD_Hatswitch\n");
-                            IOHIDDeviceGetValue(hid_device, element, &valueRef);
+                            valueRef = NULL;
+                            if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
+                                return;
+                            if (valueRef == NULL)
+                                return;
                             val = IOHIDValueGetIntegerValue(valueRef);
                             oldVal = device->generic.js.rgdwPOV[pov_idx];
                             if (val >= 8)
@@ -850,7 +866,11 @@ static void poll_osx_device_state(LPDIRECTINPUTDEVICE8A iface)
                         {
                             int wine_obj = -1;
 
-                            IOHIDDeviceGetValue(hid_device, element, &valueRef);
+                            valueRef = NULL;
+                            if (IOHIDDeviceGetValue(hid_device, element, &valueRef) != kIOReturnSuccess)
+                                return;
+                            if (valueRef == NULL)
+                                return;
                             val = IOHIDValueGetIntegerValue(valueRef);
                             newVal = joystick_map_axis(&device->generic.props[idx], val);
                             switch (usage)
@@ -945,6 +965,7 @@ static DWORD make_vid_pid(IOHIDDeviceRef device)
 static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
 {
     IOHIDDeviceRef device;
+    BOOL is_joystick;
 
     TRACE("dwDevType %u dwFlags 0x%08x version 0x%04x id %d\n", dwDevType, dwFlags, version, id);
 
@@ -953,7 +974,7 @@ static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINS
     device = get_device_ref(id);
 
     if ((dwDevType == 0) ||
-    ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
+    ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
     (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800)))
     {
         if (dwFlags & DIEDFL_FORCEFEEDBACK) {
@@ -962,16 +983,19 @@ static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINS
             if(get_ff(device, NULL) != S_OK)
                 return S_FALSE;
         }
+        is_joystick = get_device_property_long(device, CFSTR(kIOHIDDeviceUsageKey)) == kHIDUsage_GD_Joystick;
         /* Return joystick */
         lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
         lpddi->guidInstance.Data3 = id;
-        lpddi->guidProduct = DInput_Wine_OsX_Joystick_GUID;
+        lpddi->guidProduct = DInput_PIDVID_Product_GUID;
         lpddi->guidProduct.Data1 = make_vid_pid(device);
-        /* we only support traditional joysticks for now */
-        if (version >= 0x0800)
-            lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
+        lpddi->dwDevType = get_device_type(version, is_joystick);
+        lpddi->dwDevType |= DIDEVTYPE_HID;
+        lpddi->wUsagePage = 0x01; /* Desktop */
+        if (is_joystick)
+            lpddi->wUsage = 0x04; /* Joystick */
         else
-            lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
+            lpddi->wUsage = 0x05; /* Game Pad */
         sprintf(lpddi->tszInstanceName, "Joystick %d", id);
 
         /* get the device name */
@@ -989,6 +1013,7 @@ static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINS
     char name[MAX_PATH];
     char friendly[32];
     IOHIDDeviceRef device;
+    BOOL is_joystick;
 
     TRACE("dwDevType %u dwFlags 0x%08x version 0x%04x id %d\n", dwDevType, dwFlags, version, id);
 
@@ -997,7 +1022,7 @@ static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINS
     device = get_device_ref(id);
 
     if ((dwDevType == 0) ||
-    ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
+    ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
     (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
 
         if (dwFlags & DIEDFL_FORCEFEEDBACK) {
@@ -1006,18 +1031,22 @@ static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINS
             if(get_ff(device, NULL) != S_OK)
                 return S_FALSE;
         }
+        is_joystick = get_device_property_long(device, CFSTR(kIOHIDDeviceUsageKey)) == kHIDUsage_GD_Joystick;
         /* Return joystick */
         lpddi->guidInstance = DInput_Wine_OsX_Joystick_GUID;
         lpddi->guidInstance.Data3 = id;
-        lpddi->guidProduct = DInput_Wine_OsX_Joystick_GUID;
+        lpddi->guidProduct = DInput_PIDVID_Product_GUID;
         lpddi->guidProduct.Data1 = make_vid_pid(device);
-        /* we only support traditional joysticks for now */
-        if (version >= 0x0800)
-            lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
+        lpddi->dwDevType = get_device_type(version, is_joystick);
+        lpddi->dwDevType |= DIDEVTYPE_HID;
+        lpddi->wUsagePage = 0x01; /* Desktop */
+        if (is_joystick)
+            lpddi->wUsage = 0x04; /* Joystick */
         else
-            lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
+            lpddi->wUsage = 0x05; /* Game Pad */
         sprintf(friendly, "Joystick %d", id);
         MultiByteToWideChar(CP_ACP, 0, friendly, -1, lpddi->tszInstanceName, MAX_PATH);
+
         /* get the device name */
         get_osx_device_name(id, name, MAX_PATH);
 
@@ -1082,7 +1111,7 @@ static HRESULT alloc_device(REFGUID rguid, IDirectInputImpl *dinput,
 
     newDevice->generic.guidInstance = DInput_Wine_OsX_Joystick_GUID;
     newDevice->generic.guidInstance.Data3 = index;
-    newDevice->generic.guidProduct = DInput_Wine_OsX_Joystick_GUID;
+    newDevice->generic.guidProduct = DInput_PIDVID_Product_GUID;
     newDevice->generic.guidProduct.Data1 = make_vid_pid(device);
     newDevice->generic.joy_polldev = poll_osx_device_state;
 
@@ -1327,6 +1356,53 @@ static HRESULT joydev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REF
     return DIERR_DEVICENOTREG;
 }
 
+static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph)
+{
+    JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
+
+    TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(rguid), pdiph);
+    _dump_DIPROPHEADER(pdiph);
+
+    if (!IS_DIPROP(rguid)) return DI_OK;
+
+    switch (LOWORD(rguid)) {
+    case (DWORD_PTR) DIPROP_GUIDANDPATH:
+    {
+        static const WCHAR formatW[] = {'\\','\\','?','\\','h','i','d','#','v','i','d','_','%','0','4','x','&',
+                                        'p','i','d','_','%','0','4','x','&','%','s','_','%','i',0};
+        static const WCHAR miW[] = {'m','i',0};
+        static const WCHAR igW[] = {'i','g',0};
+
+        BOOL is_gamepad;
+        IOHIDDeviceRef device = get_device_ref(This->id);
+        LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph;
+        WORD vid = get_device_property_long(device, CFSTR(kIOHIDVendorIDKey));
+        WORD pid = get_device_property_long(device, CFSTR(kIOHIDProductIDKey));
+
+        if (!pid || !vid)
+            return DIERR_UNSUPPORTED;
+
+        is_gamepad = is_xinput_device(&This->generic.devcaps, vid, pid);
+        pd->guidClass = GUID_DEVCLASS_HIDCLASS;
+        sprintfW(pd->wszPath, formatW, vid, pid, is_gamepad ? igW : miW, This->id);
+
+        TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
+        break;
+    }
+
+    default:
+        return JoystickWGenericImpl_GetProperty(iface, rguid, pdiph);
+    }
+
+    return DI_OK;
+}
+
+static HRESULT WINAPI JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPDIPROPHEADER pdiph)
+{
+    JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
+    return JoystickWImpl_GetProperty(IDirectInputDevice8W_from_impl(This), rguid, pdiph);
+}
+
 static HRESULT osx_set_autocenter(JoystickImpl *This,
         const DIPROPDWORD *header)
 {
@@ -1418,7 +1494,7 @@ static HRESULT WINAPI JoystickWImpl_CreateEffect(IDirectInputDevice8W *iface,
     EffectImpl *effect;
     HRESULT hr;
 
-    TRACE("%p %s %p %p %p\n", iface, debugstr_guid(type), params, out, outer);
+    TRACE("(%p)->(%s %p %p %p)\n", This, debugstr_guid(type), params, out, outer);
     dump_DIEFFECT(params, type, 0);
 
     if(!This->ff){
@@ -1459,7 +1535,7 @@ static HRESULT WINAPI JoystickAImpl_CreateEffect(IDirectInputDevice8A *iface,
 {
     JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
 
-    TRACE("%p %s %p %p %p\n", iface, debugstr_guid(type), params, out, outer);
+    TRACE("(%p)->(%s %p %p %p)\n", This, debugstr_guid(type), params, out, outer);
 
     return JoystickWImpl_CreateEffect(&This->generic.base.IDirectInputDevice8W_iface,
             type, params, out, outer);
@@ -1509,7 +1585,7 @@ static const IDirectInputDevice8AVtbl JoystickAvt =
     IDirectInputDevice2AImpl_Release,
     JoystickAGenericImpl_GetCapabilities,
     IDirectInputDevice2AImpl_EnumObjects,
-    JoystickAGenericImpl_GetProperty,
+    JoystickAImpl_GetProperty,
     JoystickAImpl_SetProperty,
     IDirectInputDevice2AImpl_Acquire,
     IDirectInputDevice2AImpl_Unacquire,
@@ -1545,7 +1621,7 @@ static const IDirectInputDevice8WVtbl JoystickWvt =
     IDirectInputDevice2WImpl_Release,
     JoystickWGenericImpl_GetCapabilities,
     IDirectInputDevice2WImpl_EnumObjects,
-    JoystickWGenericImpl_GetProperty,
+    JoystickWImpl_GetProperty,
     JoystickWImpl_SetProperty,
     IDirectInputDevice2WImpl_Acquire,
     IDirectInputDevice2WImpl_Unacquire,
index e758cac..545304e 100644 (file)
@@ -33,6 +33,9 @@
 #define MAX_PROPS 164
 struct JoystickGenericImpl;
 
+/* Number of buttons for which to allow remapping */
+#define MAX_MAP_BUTTONS 32
+
 typedef void joy_polldev_handler(LPDIRECTINPUTDEVICE8A iface);
 
 typedef struct JoystickGenericImpl
@@ -47,6 +50,7 @@ typedef struct JoystickGenericImpl
     char        *name;
     int         device_axis_count;      /* Total number of axes in the device */
     int        *axis_map;               /* User axes remapping */
+    int         button_map[MAX_MAP_BUTTONS]; /* User button remapping */
     LONG        deadzone;               /* Default dead-zone */
 
     joy_polldev_handler *joy_polldev;
@@ -96,5 +100,6 @@ HRESULT WINAPI JoystickWGenericImpl_SetActionMap(LPDIRECTINPUTDEVICE8W iface, LP
 
 DWORD typeFromGUID(REFGUID guid) DECLSPEC_HIDDEN;
 void dump_DIEFFECT(LPCDIEFFECT eff, REFGUID guid, DWORD dwFlags) DECLSPEC_HIDDEN;
+BOOL is_xinput_device(const DIDEVCAPS *devcaps, WORD vid, WORD pid) DECLSPEC_HIDDEN;
 
 #endif /* __WINE_DLLS_DINPUT_JOYSTICK_PRIVATE_H */
index 642d0c0..0747f5d 100644 (file)
@@ -215,6 +215,9 @@ static HRESULT keyboarddev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVI
   if (id != 0)
     return E_FAIL;
 
+  if (dwFlags & DIEDFL_FORCEFEEDBACK)
+    return S_FALSE;
+
   if ((dwDevType == 0) ||
       ((dwDevType == DIDEVTYPE_KEYBOARD) && (version < 0x0800)) ||
       (((dwDevType == DI8DEVCLASS_KEYBOARD) || (dwDevType == DI8DEVTYPE_KEYBOARD)) && (version >= 0x0800))) {
@@ -233,6 +236,9 @@ static HRESULT keyboarddev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVI
   if (id != 0)
     return E_FAIL;
 
+  if (dwFlags & DIEDFL_FORCEFEEDBACK)
+    return S_FALSE;
+
   if ((dwDevType == 0) ||
       ((dwDevType == DIDEVTYPE_KEYBOARD) && (version < 0x0800)) ||
       (((dwDevType == DI8DEVCLASS_KEYBOARD) || (dwDevType == DI8DEVTYPE_KEYBOARD)) && (version >= 0x0800))) {
@@ -520,11 +526,6 @@ static HRESULT WINAPI SysKeyboardAImpl_GetDeviceInfo(
     SysKeyboardImpl *This = impl_from_IDirectInputDevice8A(iface);
     TRACE("(this=%p,%p)\n", This, pdidi);
 
-    if (pdidi->dwSize != sizeof(DIDEVICEINSTANCEA)) {
-        WARN(" dinput3 not supported yet...\n");
-       return DI_OK;
-    }
-
     fill_keyboard_dideviceinstanceA(pdidi, This->base.dinput->dwVersion, This->subtype);
     
     return DI_OK;
@@ -553,7 +554,7 @@ static HRESULT WINAPI SysKeyboardWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface,
 {
     SysKeyboardImpl *This = impl_from_IDirectInputDevice8W(iface);
 
-    TRACE("(%p) %s,%p\n", iface, debugstr_guid(rguid), pdiph);
+    TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(rguid), pdiph);
     _dump_DIPROPHEADER(pdiph);
 
     if (!IS_DIPROP(rguid)) return DI_OK;
@@ -619,7 +620,8 @@ static HRESULT WINAPI SysKeyboardWImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W ifac
                                                       LPCWSTR lpszUserName,
                                                       DWORD dwFlags)
 {
-    FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
+    SysKeyboardImpl *This = impl_from_IDirectInputDevice8W(iface);
+    FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", This, lpdiaf, debugstr_w(lpszUserName), dwFlags);
 
     return  _build_action_map(iface, lpdiaf, lpszUserName, dwFlags, DIKEYBOARD_MASK, &c_dfDIKeyboard);
 }
@@ -659,7 +661,8 @@ static HRESULT WINAPI SysKeyboardWImpl_SetActionMap(LPDIRECTINPUTDEVICE8W iface,
                                                     LPCWSTR lpszUserName,
                                                     DWORD dwFlags)
 {
-    FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
+    SysKeyboardImpl *This = impl_from_IDirectInputDevice8W(iface);
+    FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", This, lpdiaf, debugstr_w(lpszUserName), dwFlags);
 
     return _set_action_map(iface, lpdiaf, lpszUserName, dwFlags, &c_dfDIKeyboard);
 }
@@ -687,6 +690,8 @@ static HRESULT WINAPI SysKeyboardAImpl_SetActionMap(LPDIRECTINPUTDEVICE8A iface,
 
     hr = SysKeyboardWImpl_SetActionMap(&This->base.IDirectInputDevice8W_iface, &diafW, lpszUserNameW, dwFlags);
 
+    lpdiaf->dwCRC = diafW.dwCRC;
+
     HeapFree(GetProcessHeap(), 0, diafW.rgoAction);
     HeapFree(GetProcessHeap(), 0, lpszUserNameW);
 
index 7133359..ef408bb 100644 (file)
@@ -28,6 +28,7 @@
 #include "windef.h"
 #include "winbase.h"
 #include "wingdi.h"
+#include "wine/winternl.h"
 #include "winuser.h"
 #include "winerror.h"
 #include "winreg.h"
@@ -157,6 +158,9 @@ static HRESULT mousedev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEI
     if (id != 0)
         return E_FAIL;
 
+    if (dwFlags & DIEDFL_FORCEFEEDBACK)
+        return S_FALSE;
+
     if ((dwDevType == 0) ||
        ((dwDevType == DIDEVTYPE_MOUSE) && (version < 0x0800)) ||
        (((dwDevType == DI8DEVCLASS_POINTER) || (dwDevType == DI8DEVTYPE_MOUSE)) && (version >= 0x0800))) {
@@ -175,6 +179,9 @@ static HRESULT mousedev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEI
     if (id != 0)
         return E_FAIL;
 
+    if (dwFlags & DIEDFL_FORCEFEEDBACK)
+        return S_FALSE;
+
     if ((dwDevType == 0) ||
        ((dwDevType == DIDEVTYPE_MOUSE) && (version < 0x0800)) ||
        (((dwDevType == DI8DEVCLASS_POINTER) || (dwDevType == DI8DEVTYPE_MOUSE)) && (version >= 0x0800))) {
@@ -211,9 +218,9 @@ static SysMouseImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput)
     get_app_key(&hkey, &appkey);
     if (!get_config_key(hkey, appkey, "MouseWarpOverride", buffer, sizeof(buffer)))
     {
-        if (!strcasecmp(buffer, "disable"))
+        if (!_strnicmp(buffer, "disable", -1))
             newDevice->warp_override = WARP_DISABLE;
-        else if (!strcasecmp(buffer, "force"))
+        else if (!_strnicmp(buffer, "force", -1))
             newDevice->warp_override = WARP_FORCE_ON;
     }
     if (appkey) RegCloseKey(appkey);
@@ -723,6 +730,9 @@ static HRESULT WINAPI SysMouseWImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8W iface,
     else if (pdidoi->dwType & DIDFT_BUTTON)
         wsprintfW(pdidoi->tszName, buttonW, DIDFT_GETINSTANCE(pdidoi->dwType) - 3);
 
+    if(pdidoi->dwType & DIDFT_AXIS)
+        pdidoi->dwFlags |= DIDOI_ASPECTPOSITION;
+
     _dump_OBJECTINSTANCEW(pdidoi);
     return res;
 }
@@ -758,11 +768,6 @@ static HRESULT WINAPI SysMouseAImpl_GetDeviceInfo(
     SysMouseImpl *This = impl_from_IDirectInputDevice8A(iface);
     TRACE("(this=%p,%p)\n", This, pdidi);
 
-    if (pdidi->dwSize != sizeof(DIDEVICEINSTANCEA)) {
-        WARN(" dinput3 not supported yet...\n");
-       return DI_OK;
-    }
-
     fill_mouse_dideviceinstanceA(pdidi, This->base.dinput->dwVersion);
     
     return DI_OK;
@@ -828,7 +833,8 @@ static HRESULT WINAPI SysMouseWImpl_SetActionMap(LPDIRECTINPUTDEVICE8W iface,
                                                  LPCWSTR lpszUserName,
                                                  DWORD dwFlags)
 {
-    FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
+    SysMouseImpl *This = impl_from_IDirectInputDevice8W(iface);
+    FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", This, lpdiaf, debugstr_w(lpszUserName), dwFlags);
 
     return _set_action_map(iface, lpdiaf, lpszUserName, dwFlags, &c_dfDIMouse2);
 }
@@ -856,6 +862,8 @@ static HRESULT WINAPI SysMouseAImpl_SetActionMap(LPDIRECTINPUTDEVICE8A iface,
 
     hr = SysMouseWImpl_SetActionMap(&This->base.IDirectInputDevice8W_iface, &diafW, lpszUserNameW, dwFlags);
 
+    lpdiaf->dwCRC = diafW.dwCRC;
+
     HeapFree(GetProcessHeap(), 0, diafW.rgoAction);
     HeapFree(GetProcessHeap(), 0, lpszUserNameW);
 
index 9504beb..d769330 100644 (file)
@@ -2,8 +2,6 @@
 #ifndef __WINE_DINPUT_PRECOMP_H
 #define __WINE_DINPUT_PRECOMP_H
 
-#include <wine/config.h>
-
 #define WIN32_NO_STATUS
 #define _INC_WINDOWS
 #define COM_NO_WINDOWS_H
index 7fc878f..4146f4e 100644 (file)
@@ -30,7 +30,7 @@ dll/directx/wine/d3dx9_24 => 43 # Synced to WineStaging-4.0
 dll/directx/wine/d3dxof         # Synced to WineStaging-3.17
 dll/directx/wine/ddraw          # Synced to WineStaging-3.3
 dll/directx/wine/devenum        # Synced to WineStaging-4.18
-dll/directx/wine/dinput         # Synced to WineStaging-4.0
+dll/directx/wine/dinput         # Synced to WineStaging-4.18
 dll/directx/wine/dinput8        # Synced to WineStaging-3.3
 dll/directx/wine/dmusic         # Synced to WineStaging-4.0
 dll/directx/wine/dplay          # Synced to WineStaging-3.3