[PATCH] dinput: Use heuristics to guess if a device is a gamepad or a joystick

Andrew Eikum aeikum at codeweavers.com
Tue May 7 08:59:38 CDT 2019


Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47123
Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>
---

Applies on top of Alistair's series.

 dlls/dinput/dinput_private.h      |  2 +-
 dlls/dinput/joystick.c            |  6 +++-
 dlls/dinput/joystick_linux.c      | 46 +++++++++++++++++++++++++++++--
 dlls/dinput/joystick_linuxinput.c | 27 ++++++++++++++++--
 dlls/dinput/joystick_osx.c        | 20 ++++++++++++--
 5 files changed, 91 insertions(+), 10 deletions(-)

diff --git a/dlls/dinput/dinput_private.h b/dlls/dinput/dinput_private.h
index c97a49334ad..289b2f1f63d 100644
--- a/dlls/dinput/dinput_private.h
+++ b/dlls/dinput/dinput_private.h
@@ -81,7 +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) DECLSPEC_HIDDEN;
+extern DWORD get_device_type(DWORD version, BOOL is_joystick) DECLSPEC_HIDDEN;
 
 #define IS_DIPROP(x)    (((ULONG_PTR)(x) >> 16) == 0)
 
diff --git a/dlls/dinput/joystick.c b/dlls/dinput/joystick.c
index 5129bb85457..8b3edb2c540 100644
--- a/dlls/dinput/joystick.c
+++ b/dlls/dinput/joystick.c
@@ -76,8 +76,12 @@ DWORD typeFromGUID(REFGUID guid)
     }
 }
 
-DWORD get_device_type(DWORD version)
+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);
 }
diff --git a/dlls/dinput/joystick_linux.c b/dlls/dinput/joystick_linux.c
index 2644ed66a4e..b708db3e81c 100644
--- a/dlls/dinput/joystick_linux.c
+++ b/dlls/dinput/joystick_linux.c
@@ -84,6 +84,8 @@ struct JoyDev
     int  *dev_axes_map;
 
     WORD vendor_id, product_id, bus_type;
+
+    BOOL is_joystick;
 };
 
 typedef struct JoystickImpl JoystickImpl;
@@ -177,6 +179,7 @@ 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];
 
         snprintf(joydev.device, sizeof(joydev.device), "%s%d", JOYDEV_NEW, i);
         if ((fd = open(joydev.device, O_RDONLY)) == -1)
@@ -220,6 +223,42 @@ 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;
+                default:
+                    break;
+                }
+            }
+        }
+
         if (ioctl(fd, JSIOCGAXMAP, axes_map) < 0)
         {
             WARN("ioctl(%s,JSIOCGAXMAP) failed: %s\n", joydev.device, strerror(errno));
@@ -322,7 +361,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;
-    lpddi->dwDevType = get_device_type(version);
+    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 &&
@@ -330,7 +369,10 @@ static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD ver
     {
         lpddi->dwDevType |= DIDEVTYPE_HID;
         lpddi->wUsagePage = 0x01; /* Desktop */
-        lpddi->wUsage = 0x05; /* Game Pad */
+        if (joystick_devices[id].is_joystick)
+            lpddi->wUsage = 0x04; /* Joystick */
+        else
+            lpddi->wUsage = 0x05; /* Game Pad */
     }
 
     MultiByteToWideChar(CP_ACP, 0, joystick_devices[id].name, -1, lpddi->tszInstanceName, MAX_PATH);
diff --git a/dlls/dinput/joystick_linuxinput.c b/dlls/dinput/joystick_linuxinput.c
index c742c151c7a..d4512dc5ec1 100644
--- a/dlls/dinput/joystick_linuxinput.c
+++ b/dlls/dinput/joystick_linuxinput.c
@@ -100,7 +100,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 */
@@ -243,6 +243,24 @@ static void find_joydevs(void)
             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_DEAD);
+
         if (!(joydev.device = HeapAlloc(GetProcessHeap(), 0, strlen(buf) + 1)))
         {
             close(fd);
@@ -350,7 +368,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;
-    lpddi->dwDevType = get_device_type(version);
+    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 &&
@@ -358,7 +376,10 @@ static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD ver
     {
         lpddi->dwDevType |= DIDEVTYPE_HID;
         lpddi->wUsagePage = 0x01; /* Desktop */
-        lpddi->wUsage = 0x05; /* Game Pad */
+        if (joydevs[id].is_joystick)
+            lpddi->wUsage = 0x04; /* Joystick */
+        else
+            lpddi->wUsage = 0x05; /* Game Pad */
     }
 
     MultiByteToWideChar(CP_ACP, 0, joydevs[id].name, -1, lpddi->tszInstanceName, MAX_PATH);
diff --git a/dlls/dinput/joystick_osx.c b/dlls/dinput/joystick_osx.c
index aad60765cfb..a448818bb9c 100644
--- a/dlls/dinput/joystick_osx.c
+++ b/dlls/dinput/joystick_osx.c
@@ -945,6 +945,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);
 
@@ -962,15 +963,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.Data1 = make_vid_pid(device);
-        lpddi->dwDevType = get_device_type(version);
+        lpddi->dwDevType = get_device_type(version, is_joystick);
         lpddi->dwDevType |= DIDEVTYPE_HID;
         lpddi->wUsagePage = 0x01; /* Desktop */
-        lpddi->wUsage = 0x05; /* Game Pad */
+        if (is_joystick)
+            lpddi->wUsage = 0x04; /* Joystick */
+        else
+            lpddi->wUsage = 0x05; /* Game Pad */
         sprintf(lpddi->tszInstanceName, "Joystick %d", id);
 
         /* get the device name */
@@ -988,6 +993,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);
 
@@ -1005,14 +1011,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.Data1 = make_vid_pid(device);
-        lpddi->dwDevType = get_device_type(version);
+        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->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);
 
-- 
2.21.0




More information about the wine-devel mailing list