[PATCH v3] dinput: Introduce new HID joystick backend.

Rémi Bernon rbernon at codeweavers.com
Wed Jun 30 03:54:19 CDT 2021


This adds a new joystick backend, implemented on top of HID and without
any host dependencies. This will be progressively implementated, and
it's not going to be usable until at least a few more patches.

Because of that, and because it may also introduce regressions compared
to the existing backends, it is disabled by default and is optionally
enabled using the following global registry key:

  [HKCU\\Software\\Wine\\DirectInput\\Joysticks]
  "HID"="enabled"

Or using the corresponding AppDefaults registry key:

  [HKCU\\Software\\Wine\\AppDefaults\\<app.exe>\\DirectInput\\Joysticks]
  "HID"="enabled"

This setting will be removed later, when it becomes usable enough, to
use the individual device disable mechanism available in joy.cpl.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---

v3: Use setupapi directly, as user32 api usage was confusing. This also
    now matches device on either their index, HID handle or vid_pid,
    depending on what's been provided.

    The instance GUID now includes the HID handle, which will be more
    stable than the device index in setupapi device list.

    Matching the vid_pid assumes that setupapi device path is upper
    case, which I think is the case. We could make sure otherwise but
    I don't think we have to and it's not very convenient without wcsupr.

 dlls/dinput/Makefile.in           |   3 +-
 dlls/dinput/dinput_main.c         |   3 +-
 dlls/dinput/dinput_private.h      |   1 +
 dlls/dinput/joystick.c            |  17 +-
 dlls/dinput/joystick_hid.c        | 491 ++++++++++++++++++++++++++++++
 dlls/dinput/joystick_linux.c      |   2 +-
 dlls/dinput/joystick_linuxinput.c |   2 +-
 dlls/dinput/joystick_private.h    |   2 +-
 dlls/dinput8/Makefile.in          |   3 +-
 9 files changed, 513 insertions(+), 11 deletions(-)
 create mode 100644 dlls/dinput/joystick_hid.c

diff --git a/dlls/dinput/Makefile.in b/dlls/dinput/Makefile.in
index a22c8a72180..29d3e8ece65 100644
--- a/dlls/dinput/Makefile.in
+++ b/dlls/dinput/Makefile.in
@@ -1,6 +1,6 @@
 MODULE    = dinput.dll
 IMPORTLIB = dinput
-IMPORTS   = dinput dxguid uuid comctl32 ole32 user32 advapi32
+IMPORTS   = dinput dxguid uuid comctl32 ole32 user32 advapi32 hid setupapi
 EXTRADEFS = -DDIRECTINPUT_VERSION=0x0700
 EXTRALIBS = $(IOKIT_LIBS) $(FORCEFEEDBACK_LIBS)
 
@@ -12,6 +12,7 @@ C_SRCS = \
 	dinput_main.c \
 	effect_linuxinput.c \
 	joystick.c \
+	joystick_hid.c \
 	joystick_linux.c \
 	joystick_linuxinput.c \
 	joystick_osx.c \
diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c
index c7b932aca44..88ee1855675 100644
--- a/dlls/dinput/dinput_main.c
+++ b/dlls/dinput/dinput_main.c
@@ -80,7 +80,8 @@ static const struct dinput_device *dinput_devices[] =
     &keyboard_device,
     &joystick_linuxinput_device,
     &joystick_linux_device,
-    &joystick_osx_device
+    &joystick_osx_device,
+    &joystick_hid_device,
 };
 
 HINSTANCE DINPUT_instance;
diff --git a/dlls/dinput/dinput_private.h b/dlls/dinput/dinput_private.h
index 7e0f56c68df..c11b64585d9 100644
--- a/dlls/dinput/dinput_private.h
+++ b/dlls/dinput/dinput_private.h
@@ -67,6 +67,7 @@ struct DevicePlayer {
 
 extern const struct dinput_device mouse_device DECLSPEC_HIDDEN;
 extern const struct dinput_device keyboard_device DECLSPEC_HIDDEN;
+extern const struct dinput_device joystick_hid_device DECLSPEC_HIDDEN;
 extern const struct dinput_device joystick_linux_device DECLSPEC_HIDDEN;
 extern const struct dinput_device joystick_linuxinput_device DECLSPEC_HIDDEN;
 extern const struct dinput_device joystick_osx_device DECLSPEC_HIDDEN;
diff --git a/dlls/dinput/joystick.c b/dlls/dinput/joystick.c
index 8ea7850621c..60153d0d0f3 100644
--- a/dlls/dinput/joystick.c
+++ b/dlls/dinput/joystick.c
@@ -271,13 +271,13 @@ void dump_DIEFFECT(LPCDIEFFECT eff, REFGUID guid, DWORD dwFlags)
     }
 }
 
-BOOL device_disabled_registry(const char* name)
+BOOL device_disabled_registry(const char* name, BOOL disable)
 {
     static const char disabled_str[] = "disabled";
+    static const char enabled_str[] = "enabled";
     static const char joystick_key[] = "Joysticks";
     char buffer[MAX_PATH];
     HKEY hkey, appkey, temp;
-    BOOL do_disable = FALSE;
 
     get_app_key(&hkey, &appkey);
 
@@ -297,16 +297,23 @@ BOOL device_disabled_registry(const char* name)
 
     /* Look for the "controllername"="disabled" key */
     if (!get_config_key(hkey, appkey, name, buffer, sizeof(buffer)))
-        if (!strcmp(disabled_str, buffer))
+    {
+        if (!disable && !strcmp(disabled_str, buffer))
         {
             TRACE("Disabling joystick '%s' based on registry key.\n", name);
-            do_disable = TRUE;
+            disable = TRUE;
+        }
+        else if (disable && !strcmp(enabled_str, buffer))
+        {
+            TRACE("Enabling joystick '%s' based on registry key.\n", name);
+            disable = FALSE;
         }
+    }
 
     if (appkey) RegCloseKey(appkey);
     if (hkey)   RegCloseKey(hkey);
 
-    return do_disable;
+    return disable;
 }
 
 BOOL is_xinput_device(const DIDEVCAPS *devcaps, WORD vid, WORD pid)
diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c
new file mode 100644
index 00000000000..f29c806a51d
--- /dev/null
+++ b/dlls/dinput/joystick_hid.c
@@ -0,0 +1,491 @@
+/*  DirectInput HID Joystick device
+ *
+ * Copyright 2021 Rémi Bernon for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winternl.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "winreg.h"
+
+#include "ddk/hidsdi.h"
+#include "setupapi.h"
+#include "devguid.h"
+#include "dinput.h"
+#include "setupapi.h"
+
+#include "wine/debug.h"
+
+#include "dinput_private.h"
+#include "device_private.h"
+#include "joystick_private.h"
+
+#include "initguid.h"
+#include "devpkey.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(dinput);
+
+DEFINE_GUID( hid_joystick_guid, 0x9e573edb, 0x7734, 0x11d2, 0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7 );
+DEFINE_DEVPROPKEY( DEVPROPKEY_HID_HANDLE, 0xbc62e415, 0xf4fe, 0x405c, 0x8e, 0xda, 0x63, 0x6f, 0xb5, 0x9f, 0x08, 0x98, 2 );
+
+struct hid_joystick
+{
+    IDirectInputDeviceImpl base;
+
+    HANDLE device;
+    PHIDP_PREPARSED_DATA preparsed;
+};
+
+static inline struct hid_joystick *impl_from_IDirectInputDevice8W( IDirectInputDevice8W *iface )
+{
+    return CONTAINING_RECORD( CONTAINING_RECORD( iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface ),
+                              struct hid_joystick, base );
+}
+
+static ULONG WINAPI hid_joystick_Release( IDirectInputDevice8W *iface )
+{
+    struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
+    struct hid_joystick tmp = *impl;
+    ULONG res;
+
+    if (!(res = IDirectInputDevice2WImpl_Release( iface )))
+    {
+        HidD_FreePreparsedData( tmp.preparsed );
+        CloseHandle( tmp.device );
+    }
+
+    return res;
+}
+
+static HRESULT WINAPI hid_joystick_GetCapabilities( IDirectInputDevice8W *iface, DIDEVCAPS *caps )
+{
+    FIXME( "iface %p, caps %p stub!\n", iface, caps );
+
+    if (!caps) return E_POINTER;
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI hid_joystick_EnumObjects( IDirectInputDevice8W *iface, LPDIENUMDEVICEOBJECTSCALLBACKW callback,
+                                                void *ref, DWORD flags )
+{
+    FIXME( "iface %p, callback %p, ref %p, flags %#x stub!\n", iface, callback, ref, flags );
+
+    if (!callback) return DIERR_INVALIDPARAM;
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI hid_joystick_GetProperty( IDirectInputDevice8W *iface, REFGUID guid, DIPROPHEADER *header )
+{
+    FIXME( "iface %p, guid %s, header %p stub!\n", iface, debugstr_guid( guid ), header );
+
+    if (!header) return DIERR_INVALIDPARAM;
+    if (!IS_DIPROP( guid )) return DI_OK;
+
+    switch (LOWORD( guid ))
+    {
+    default: return IDirectInputDevice2WImpl_GetProperty( iface, guid, header );
+    }
+}
+
+static HRESULT WINAPI hid_joystick_SetProperty( IDirectInputDevice8W *iface, REFGUID guid, const DIPROPHEADER *header )
+{
+    FIXME( "iface %p, guid %s, header %p stub!\n", iface, debugstr_guid( guid ), header );
+
+    if (!header) return DIERR_INVALIDPARAM;
+    if (!IS_DIPROP( guid )) return DI_OK;
+
+    switch (LOWORD( guid ))
+    {
+    default: return IDirectInputDevice2WImpl_SetProperty( iface, guid, header );
+    }
+}
+
+static HRESULT WINAPI hid_joystick_Acquire( IDirectInputDevice8W *iface )
+{
+    HRESULT hr;
+
+    TRACE( "iface %p.\n", iface );
+
+    if ((hr = IDirectInputDevice2WImpl_Acquire( iface )) != DI_OK) return hr;
+
+    return DI_OK;
+}
+
+static HRESULT WINAPI hid_joystick_Unacquire( IDirectInputDevice8W *iface )
+{
+    HRESULT hr;
+
+    TRACE( "iface %p.\n", iface );
+
+    if ((hr = IDirectInputDevice2WImpl_Unacquire( iface )) != DI_OK) return hr;
+
+    return DI_OK;
+}
+
+static HRESULT WINAPI hid_joystick_GetDeviceState( IDirectInputDevice8W *iface, DWORD len, void *ptr )
+{
+    FIXME( "iface %p, len %u, ptr %p stub!\n", iface, len, ptr );
+
+    if (!ptr) return DIERR_INVALIDPARAM;
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI hid_joystick_GetObjectInfo( IDirectInputDevice8W *iface, DIDEVICEOBJECTINSTANCEW *instance,
+                                                  DWORD obj, DWORD how )
+{
+    FIXME( "iface %p, instance %p, obj %#x, how %#x stub!\n", iface, instance, obj, how );
+
+    if (!instance) return E_POINTER;
+    if (instance->dwSize != sizeof(DIDEVICEOBJECTINSTANCE_DX3W) &&
+        instance->dwSize != sizeof(DIDEVICEOBJECTINSTANCEW))
+        return DIERR_INVALIDPARAM;
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI hid_joystick_GetDeviceInfo( IDirectInputDevice8W *iface, DIDEVICEINSTANCEW *instance )
+{
+    FIXME( "iface %p, instance %p stub!\n", iface, instance );
+
+    if (!instance) return E_POINTER;
+    if (instance->dwSize != sizeof(DIDEVICEINSTANCE_DX3W) &&
+        instance->dwSize != sizeof(DIDEVICEINSTANCEW))
+        return DIERR_INVALIDPARAM;
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI hid_joystick_CreateEffect( IDirectInputDevice8W *iface, REFGUID rguid,
+                                                 const DIEFFECT *effect, IDirectInputEffect **out,
+                                                 IUnknown *outer )
+{
+    FIXME( "iface %p, rguid %s, effect %p, out %p, outer %p stub!\n", iface, debugstr_guid( rguid ),
+           effect, out, outer );
+
+    if (!out) return E_POINTER;
+    if (!rguid || !effect) return DI_NOEFFECT;
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI hid_joystick_EnumEffects( IDirectInputDevice8W *iface, LPDIENUMEFFECTSCALLBACKW callback,
+                                                void *ref, DWORD type )
+{
+    FIXME( "iface %p, callback %p, ref %p, type %#x stub!\n", iface, callback, ref, type );
+
+    if (!callback) return DIERR_INVALIDPARAM;
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI hid_joystick_GetEffectInfo( IDirectInputDevice8W *iface, DIEFFECTINFOW *info, REFGUID guid )
+{
+    FIXME( "iface %p, info %p, guid %s stub!\n", iface, info, debugstr_guid( guid ) );
+
+    if (!info) return E_POINTER;
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI hid_joystick_GetForceFeedbackState( IDirectInputDevice8W *iface, DWORD *out )
+{
+    FIXME( "iface %p, out %p stub!\n", iface, out );
+
+    if (!out) return E_POINTER;
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI hid_joystick_SendForceFeedbackCommand( IDirectInputDevice8W *iface, DWORD flags )
+{
+    FIXME( "iface %p, flags %x stub!\n", iface, flags );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI hid_joystick_EnumCreatedEffectObjects( IDirectInputDevice8W *iface,
+                                                             LPDIENUMCREATEDEFFECTOBJECTSCALLBACK callback,
+                                                             void *ref, DWORD flags )
+{
+    FIXME( "iface %p, callback %p, ref %p, flags %#x stub!\n", iface, callback, ref, flags );
+
+    if (!callback) return DIERR_INVALIDPARAM;
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI hid_joystick_BuildActionMap( IDirectInputDevice8W *iface, DIACTIONFORMATW *format,
+                                                   const WCHAR *username, DWORD flags )
+{
+    FIXME( "iface %p, format %p, username %s, flags %#x stub!\n", iface, format, debugstr_w(username), flags );
+
+    if (!format) return DIERR_INVALIDPARAM;
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI hid_joystick_SetActionMap( IDirectInputDevice8W *iface, DIACTIONFORMATW *format,
+                                                 const WCHAR *username, DWORD flags )
+{
+    struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
+
+    TRACE( "iface %p, format %p, username %s, flags %#x.\n", iface, format, debugstr_w(username), flags );
+
+    if (!format) return DIERR_INVALIDPARAM;
+
+    return _set_action_map( iface, format, username, flags, impl->base.data_format.wine_df );
+}
+
+static const IDirectInputDevice8WVtbl hid_joystick_vtbl =
+{
+    /*** IUnknown methods ***/
+    IDirectInputDevice2WImpl_QueryInterface,
+    IDirectInputDevice2WImpl_AddRef,
+    hid_joystick_Release,
+    /*** IDirectInputDevice methods ***/
+    hid_joystick_GetCapabilities,
+    hid_joystick_EnumObjects,
+    hid_joystick_GetProperty,
+    hid_joystick_SetProperty,
+    hid_joystick_Acquire,
+    hid_joystick_Unacquire,
+    hid_joystick_GetDeviceState,
+    IDirectInputDevice2WImpl_GetDeviceData,
+    IDirectInputDevice2WImpl_SetDataFormat,
+    IDirectInputDevice2WImpl_SetEventNotification,
+    IDirectInputDevice2WImpl_SetCooperativeLevel,
+    hid_joystick_GetObjectInfo,
+    hid_joystick_GetDeviceInfo,
+    IDirectInputDevice2WImpl_RunControlPanel,
+    IDirectInputDevice2WImpl_Initialize,
+    /*** IDirectInputDevice2 methods ***/
+    hid_joystick_CreateEffect,
+    hid_joystick_EnumEffects,
+    hid_joystick_GetEffectInfo,
+    hid_joystick_GetForceFeedbackState,
+    hid_joystick_SendForceFeedbackCommand,
+    hid_joystick_EnumCreatedEffectObjects,
+    IDirectInputDevice2WImpl_Escape,
+    IDirectInputDevice2WImpl_Poll,
+    IDirectInputDevice2WImpl_SendDeviceData,
+    /*** IDirectInputDevice7 methods ***/
+    IDirectInputDevice7WImpl_EnumEffectsInFile,
+    IDirectInputDevice7WImpl_WriteEffectToFile,
+    /*** IDirectInputDevice8 methods ***/
+    hid_joystick_BuildActionMap,
+    hid_joystick_SetActionMap,
+    IDirectInputDevice8WImpl_GetImageInfo,
+};
+
+static BOOL hid_joystick_device_try_open( const WCHAR *path, HANDLE *device, PHIDP_PREPARSED_DATA *preparsed,
+                                          HIDD_ATTRIBUTES *attrs, HIDP_CAPS *caps, DIDEVICEINSTANCEW *instance )
+{
+    PHIDP_PREPARSED_DATA preparsed_data = NULL;
+    HANDLE device_file;
+
+    device_file = CreateFileW( path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                               NULL, OPEN_EXISTING, 0, 0 );
+    if (device_file == INVALID_HANDLE_VALUE) return FALSE;
+
+    if (!HidD_GetPreparsedData( device_file, &preparsed_data )) goto failed;
+    if (!HidD_GetAttributes( device_file, attrs )) goto failed;
+    if (HidP_GetCaps( preparsed_data, caps ) != HIDP_STATUS_SUCCESS) goto failed;
+
+    if (caps->UsagePage == HID_USAGE_PAGE_GAME) FIXME( "Unimplemented HID game usage page!\n" );
+    if (caps->UsagePage == HID_USAGE_PAGE_SIMULATION) FIXME( "Unimplemented HID simulation usage page!\n" );
+    if (caps->UsagePage != HID_USAGE_PAGE_GENERIC) goto failed;
+    if (caps->Usage != HID_USAGE_GENERIC_GAMEPAD && caps->Usage != HID_USAGE_GENERIC_JOYSTICK) goto failed;
+
+    if (!HidD_GetProductString( device_file, instance->tszInstanceName, MAX_PATH )) goto failed;
+    if (!HidD_GetProductString( device_file, instance->tszProductName, MAX_PATH )) goto failed;
+
+    *device = device_file;
+    *preparsed = preparsed_data;
+    return TRUE;
+
+failed:
+    CloseHandle( device_file );
+    HidD_FreePreparsedData( preparsed_data );
+    return FALSE;
+}
+
+static HRESULT hid_joystick_device_open( DWORD index, UINT32 handle, UINT32 vid_pid, WCHAR *device_path,
+                                         HANDLE *device, DWORD version, PHIDP_PREPARSED_DATA *preparsed,
+                                         HIDD_ATTRIBUTES *attrs, HIDP_CAPS *caps, DIDEVICEINSTANCEW *instance )
+{
+    static const WCHAR vid_pid_fmt_w[] = {'V','I','D','_','%','0','4','X','&','P','I','D','_','%','0','4','X',0};
+    char buffer[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + MAX_PATH * sizeof(WCHAR)];
+    SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail = (void *)buffer;
+    SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)};
+    SP_DEVINFO_DATA devinfo = {sizeof(devinfo)};
+    WCHAR vid_pid_w[18] = {0};
+    UINT32 i = 0, hid_handle;
+    HDEVINFO set;
+    DWORD type;
+    GUID hid;
+
+    TRACE( "index %u, handle %u, vid_pid %d\n", index, handle, vid_pid );
+
+    HidD_GetHidGuid( &hid );
+    snprintfW( vid_pid_w, ARRAY_SIZE(vid_pid_w), vid_pid_fmt_w, vid_pid & 0xffff, vid_pid >> 16 );
+
+    set = SetupDiGetClassDevsW( &hid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT );
+    if (set == INVALID_HANDLE_VALUE) return DIERR_DEVICENOTREG;
+
+    *device = NULL;
+    *preparsed = NULL;
+    while (SetupDiEnumDeviceInterfaces( set, NULL, &hid, i++, &iface ))
+    {
+        detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
+        if (!SetupDiGetDeviceInterfaceDetailW( set, &iface, detail, sizeof(buffer), NULL, &devinfo ))
+            continue;
+        if (!SetupDiGetDevicePropertyW( set, &devinfo, &DEVPROPKEY_HID_HANDLE, &type,
+                                        (BYTE *)&hid_handle, sizeof(hid_handle), NULL, 0 ) ||
+            type != DEVPROP_TYPE_UINT32)
+            continue;
+        if (!hid_joystick_device_try_open( detail->DevicePath, device, preparsed, attrs, caps, instance ))
+            continue;
+
+        if (!handle && !vid_pid && !index--) break;
+        if (handle && handle == hid_handle) break;
+        if (vid_pid && strstrW( detail->DevicePath, vid_pid_w )) break;
+
+        CloseHandle( *device );
+        HidD_FreePreparsedData( *preparsed );
+        *device = NULL;
+        *preparsed = NULL;
+    }
+
+    SetupDiDestroyDeviceInfoList( set );
+    if (!*device || !*preparsed) return DIERR_DEVICENOTREG;
+
+    lstrcpynW( device_path, detail->DevicePath, MAX_PATH );
+    instance->guidInstance = hid_joystick_guid;
+    instance->guidInstance.Data3 = hid_handle;
+    instance->guidProduct = DInput_PIDVID_Product_GUID;
+    instance->guidProduct.Data1 = MAKELONG( attrs->VendorID, attrs->ProductID );
+    instance->dwDevType = get_device_type( version, caps->Usage != HID_USAGE_GENERIC_GAMEPAD ) | DIDEVTYPE_HID;
+    instance->guidFFDriver = GUID_NULL;
+    instance->wUsagePage = caps->UsagePage;
+    instance->wUsage = caps->Usage;
+    return DI_OK;
+}
+
+static HRESULT hid_joystick_enum_device( DWORD type, DWORD flags, DIDEVICEINSTANCEW *instance,
+                                         DWORD version, int index )
+{
+    HIDD_ATTRIBUTES attrs = {sizeof(attrs)};
+    PHIDP_PREPARSED_DATA preparsed;
+    WCHAR device_path[MAX_PATH];
+    HIDP_CAPS caps;
+    HANDLE device;
+    HRESULT hr;
+
+    TRACE( "type %x, flags %#x, instance %p, version %04x, index %d\n", type, flags, instance, version, index );
+
+    hr = hid_joystick_device_open( index, 0, 0, device_path, &device, version,
+                                   &preparsed, &attrs, &caps, instance );
+    if (hr != DI_OK) return hr;
+
+    HidD_FreePreparsedData( preparsed );
+    CloseHandle( device );
+
+    if (instance->dwSize != sizeof(DIDEVICEINSTANCEW))
+        return S_FALSE;
+    if (version < 0x0800 && type != DIDEVTYPE_JOYSTICK)
+        return S_FALSE;
+    if (version >= 0x0800 && type != DI8DEVCLASS_ALL && type != DI8DEVCLASS_GAMECTRL)
+        return S_FALSE;
+
+    if (device_disabled_registry( "HID", TRUE ))
+        return DIERR_DEVICENOTREG;
+
+    TRACE( "Found device %s, usage %04x:%04x, product %s, instance %s, name %s\n", debugstr_w(device_path),
+           instance->wUsagePage, instance->wUsage, debugstr_guid( &instance->guidProduct ),
+           debugstr_guid( &instance->guidInstance ), debugstr_w(instance->tszInstanceName) );
+
+    return DI_OK;
+}
+
+static HRESULT hid_joystick_create_device( IDirectInputImpl *dinput, REFGUID guid, IDirectInputDevice8W **out )
+{
+    DIDEVICEINSTANCEW instance = {sizeof(instance)};
+    GUID inst_guid = *guid, prod_guid = *guid;
+    DWORD size = sizeof(struct hid_joystick);
+    HIDD_ATTRIBUTES attrs = {sizeof(attrs)};
+    struct hid_joystick *impl = NULL;
+    PHIDP_PREPARSED_DATA preparsed;
+    UINT32 handle = 0, vid_pid = 0;
+    DIDATAFORMAT *format = NULL;
+    WCHAR device_path[MAX_PATH];
+    HIDP_CAPS caps;
+    HANDLE device;
+    HRESULT hr;
+
+    TRACE( "dinput %p, guid %s, out %p\n", dinput, debugstr_guid( guid ), out );
+
+    *out = NULL;
+
+    inst_guid.Data3 = hid_joystick_guid.Data3;
+    prod_guid.Data1 = DInput_PIDVID_Product_GUID.Data1;
+    if (IsEqualGUID( &hid_joystick_guid, &inst_guid )) handle = guid->Data3;
+    else if (IsEqualGUID( &DInput_PIDVID_Product_GUID, &prod_guid )) vid_pid = guid->Data1;
+    else return DIERR_DEVICENOTREG;
+
+    hr = hid_joystick_device_open( 0, handle, vid_pid, device_path, &device, dinput->dwVersion,
+                                   &preparsed, &attrs, &caps, &instance );
+    if (hr != DI_OK) return hr;
+
+    hr = direct_input_device_alloc( size, &hid_joystick_vtbl, guid, dinput, (void **)&impl );
+    if (FAILED(hr)) goto failed;
+
+    impl->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": hid_joystick.base.crit");
+    impl->base.dwCoopLevel = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND;
+
+    impl->device = device;
+    impl->preparsed = preparsed;
+
+    if (!(format = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*format) ))) goto failed;
+    impl->base.data_format.wine_df = format;
+
+    TRACE( "Created %p\n", impl );
+
+    *out = &impl->base.IDirectInputDevice8W_iface;
+    return DI_OK;
+
+failed:
+    HeapFree( GetProcessHeap(), 0, format );
+    HeapFree( GetProcessHeap(), 0, impl );
+    HidD_FreePreparsedData( preparsed );
+    CloseHandle( device );
+    return hr;
+}
+
+const struct dinput_device joystick_hid_device =
+{
+    "Wine HID joystick driver",
+    hid_joystick_enum_device,
+    hid_joystick_create_device,
+};
diff --git a/dlls/dinput/joystick_linux.c b/dlls/dinput/joystick_linux.c
index 3215978c995..312ef3d9c42 100644
--- a/dlls/dinput/joystick_linux.c
+++ b/dlls/dinput/joystick_linux.c
@@ -175,7 +175,7 @@ static INT find_joystick_devices(void)
         /* Append driver name */
         strcat(joydev.name, JOYDEVDRIVER);
 
-        if (device_disabled_registry(joydev.name)) {
+        if (device_disabled_registry(joydev.name, FALSE)) {
             close(fd);
             continue;
         }
diff --git a/dlls/dinput/joystick_linuxinput.c b/dlls/dinput/joystick_linuxinput.c
index 2b970271ec3..558c8cc19e5 100644
--- a/dlls/dinput/joystick_linuxinput.c
+++ b/dlls/dinput/joystick_linuxinput.c
@@ -264,7 +264,7 @@ static void find_joydevs(void)
         else
             joydev.name = joydev.device;
 
-        if (device_disabled_registry(joydev.name)) {
+        if (device_disabled_registry(joydev.name, FALSE)) {
             close(fd);
             HeapFree(GetProcessHeap(), 0, joydev.name);
             if (joydev.name != joydev.device)
diff --git a/dlls/dinput/joystick_private.h b/dlls/dinput/joystick_private.h
index 874bf3e69a7..9cc30605234 100644
--- a/dlls/dinput/joystick_private.h
+++ b/dlls/dinput/joystick_private.h
@@ -57,7 +57,7 @@ HRESULT setup_dinput_options(JoystickGenericImpl *This, const int *default_axis_
 
 DWORD joystick_map_pov(const POINTL *p) DECLSPEC_HIDDEN;
 
-BOOL device_disabled_registry(const char* name) DECLSPEC_HIDDEN;
+BOOL device_disabled_registry(const char* name, BOOL disable) DECLSPEC_HIDDEN;
 
 ULONG WINAPI JoystickWGenericImpl_Release(LPDIRECTINPUTDEVICE8W iface);
 
diff --git a/dlls/dinput8/Makefile.in b/dlls/dinput8/Makefile.in
index 35b3bfb75f5..5032c7689bc 100644
--- a/dlls/dinput8/Makefile.in
+++ b/dlls/dinput8/Makefile.in
@@ -1,6 +1,6 @@
 MODULE    = dinput8.dll
 IMPORTLIB = dinput8
-IMPORTS   = dinput8 dxguid uuid comctl32 ole32 user32 advapi32
+IMPORTS   = dinput8 dxguid uuid comctl32 ole32 user32 advapi32 hid setupapi
 EXTRADEFS = -DDIRECTINPUT_VERSION=0x0800
 EXTRALIBS = $(IOKIT_LIBS) $(FORCEFEEDBACK_LIBS)
 PARENTSRC = ../dinput
@@ -13,6 +13,7 @@ C_SRCS = \
 	dinput_main.c \
 	effect_linuxinput.c \
 	joystick.c \
+	joystick_hid.c \
 	joystick_linux.c \
 	joystick_linuxinput.c \
 	joystick_osx.c \
-- 
2.32.0




More information about the wine-devel mailing list