It's accessed on Release, and this will make it easier to handle device creation failure cleanup.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
I'm resending this HID joystick implementation, I think HID layer is now in a good shape and this will be a good occasion to start testing it.
The input events should at least work, and the force feedback support otoh will require the complete winebus refactoring first.
This series is just the initial implementation, which does nothing, but I splitted and added some small refactoring first to make it simpler.
Since last time I shaved the initial implementation to only include the necessary functions, and I'll add specific implementation of new methods as they start to be implemented (instead of having stubs of everything).
The enumeration is mostly the same as the last version I sent.
dlls/dinput/device.c | 9 +++++++++ dlls/dinput/joystick_linux.c | 3 +-- dlls/dinput/joystick_linuxinput.c | 3 +-- dlls/dinput/joystick_osx.c | 3 +-- dlls/dinput/keyboard.c | 4 +--- dlls/dinput/mouse.c | 3 +-- 6 files changed, 14 insertions(+), 11 deletions(-)
diff --git a/dlls/dinput/device.c b/dlls/dinput/device.c index ac60a21326c..25084f82a9c 100644 --- a/dlls/dinput/device.c +++ b/dlls/dinput/device.c @@ -1708,11 +1708,20 @@ HRESULT direct_input_device_alloc( SIZE_T size, const IDirectInputDevice8WVtbl * const GUID *guid, IDirectInputImpl *dinput, void **out ) { IDirectInputDeviceImpl *This; + DIDATAFORMAT *format; + if (!(This = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) return DIERR_OUTOFMEMORY; + if (!(format = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*format) ))) + { + HeapFree( GetProcessHeap(), 0, This ); + return DIERR_OUTOFMEMORY; + } + This->IDirectInputDevice8A_iface.lpVtbl = &dinput_device_a_vtbl; This->IDirectInputDevice8W_iface.lpVtbl = vtbl; This->ref = 1; This->guid = *guid; + This->data_format.wine_df = format; InitializeCriticalSection( &This->crit ); This->dinput = dinput; IDirectInput_AddRef( &dinput->IDirectInput7A_iface ); diff --git a/dlls/dinput/joystick_linux.c b/dlls/dinput/joystick_linux.c index 3215978c995..258f6e9e5e0 100644 --- a/dlls/dinput/joystick_linux.c +++ b/dlls/dinput/joystick_linux.c @@ -411,6 +411,7 @@ static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, JoystickIm
if (FAILED(hr = direct_input_device_alloc( sizeof(JoystickImpl), &JoystickWvt, rguid, dinput, (void **)&newDevice ))) return hr; + df = newDevice->generic.base.data_format.wine_df; newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
newDevice->joydev = &joystick_devices[index]; @@ -438,7 +439,6 @@ static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, JoystickIm goto FAILED1;
/* Create copy of default data format */ - if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED; memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons; @@ -465,7 +465,6 @@ static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, JoystickIm df->rgodf[idx ].pguid = &GUID_Button; df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON; } - newDevice->generic.base.data_format.wine_df = df;
/* initialize default properties */ for (i = 0; i < c_dfDIJoystick2.dwNumObjs; i++) { diff --git a/dlls/dinput/joystick_linuxinput.c b/dlls/dinput/joystick_linuxinput.c index 2b970271ec3..06a735f7d91 100644 --- a/dlls/dinput/joystick_linuxinput.c +++ b/dlls/dinput/joystick_linuxinput.c @@ -407,6 +407,7 @@ static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, JoystickIm
if (FAILED(hr = direct_input_device_alloc( sizeof(JoystickImpl), &JoystickWvt, rguid, dinput, (void **)&newDevice ))) return hr; + df = newDevice->generic.base.data_format.wine_df; newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->base.crit");
newDevice->generic.joy_polldev = joy_polldev; @@ -468,7 +469,6 @@ static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, JoystickIm if (setup_dinput_options(&newDevice->generic, default_axis_map) != DI_OK) goto failed;
/* Create copy of default data format */ - if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto failed; memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize); if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto failed;
@@ -514,7 +514,6 @@ static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, JoystickIm df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(newDevice->generic.devcaps.dwButtons++) | DIDFT_PSHBUTTON; } df->dwNumObjs = idx; - newDevice->generic.base.data_format.wine_df = df;
fake_current_js_state(newDevice);
diff --git a/dlls/dinput/joystick_osx.c b/dlls/dinput/joystick_osx.c index 27a47b07639..62a063b6da1 100644 --- a/dlls/dinput/joystick_osx.c +++ b/dlls/dinput/joystick_osx.c @@ -1075,6 +1075,7 @@ static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, JoystickIm
if (FAILED(hr = direct_input_device_alloc( sizeof(JoystickImpl), &JoystickWvt, rguid, dinput, (void **)&newDevice ))) return hr; + df = newDevice->generic.base.data_format.wine_df; newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
newDevice->id = index; @@ -1138,7 +1139,6 @@ static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, JoystickIm }
/* Create copy of default data format */ - if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED; memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons; @@ -1203,7 +1203,6 @@ static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, JoystickIm df->rgodf[idx ].pguid = &GUID_Button; df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON; } - newDevice->generic.base.data_format.wine_df = df;
/* initialize default properties */ get_osx_device_elements_props(newDevice); diff --git a/dlls/dinput/keyboard.c b/dlls/dinput/keyboard.c index fc9dbdb2ed4..5d532ab8059 100644 --- a/dlls/dinput/keyboard.c +++ b/dlls/dinput/keyboard.c @@ -203,12 +203,12 @@ static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, SysKeyboar
if (FAILED(hr = direct_input_device_alloc( sizeof(SysKeyboardImpl), &SysKeyboardWvt, rguid, dinput, (void **)&newDevice ))) return hr; + df = newDevice->base.data_format.wine_df; newDevice->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": SysKeyboardImpl*->base.crit");
newDevice->subtype = get_keyboard_subtype();
/* Create copy of default data format */ - if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIKeyboard.dwSize))) goto failed; memcpy(df, &c_dfDIKeyboard, c_dfDIKeyboard.dwSize); if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto failed;
@@ -226,8 +226,6 @@ static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, SysKeyboar } df->dwNumObjs = idx;
- newDevice->base.data_format.wine_df = df; - *out = newDevice; return DI_OK;
diff --git a/dlls/dinput/mouse.c b/dlls/dinput/mouse.c index 22e40a60285..145a792f396 100644 --- a/dlls/dinput/mouse.c +++ b/dlls/dinput/mouse.c @@ -149,6 +149,7 @@ static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, SysMouseIm
if (FAILED(hr = direct_input_device_alloc( sizeof(SysMouseImpl), &SysMouseWvt, rguid, dinput, (void **)&newDevice ))) return hr; + df = newDevice->base.data_format.wine_df; newDevice->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": SysMouseImpl*->base.crit");
newDevice->base.dwCoopLevel = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND; @@ -165,7 +166,6 @@ static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, SysMouseIm if (hkey) RegCloseKey(hkey);
/* Create copy of default data format */ - if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIMouse2.dwSize))) goto failed; memcpy(df, &c_dfDIMouse2, c_dfDIMouse2.dwSize); if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto failed; memcpy(df->rgodf, c_dfDIMouse2.rgodf, df->dwNumObjs * df->dwObjSize); @@ -177,7 +177,6 @@ static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, SysMouseIm else df->rgodf[i].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
- newDevice->base.data_format.wine_df = df; if (dinput->dwVersion >= 0x0800) { newDevice->base.use_raw_input = TRUE;
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/dinput/joystick.c | 17 ++++++++++++----- dlls/dinput/joystick_linux.c | 2 +- dlls/dinput/joystick_linuxinput.c | 2 +- dlls/dinput/joystick_private.h | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-)
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_linux.c b/dlls/dinput/joystick_linux.c index 258f6e9e5e0..3a8b0f07704 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 06a735f7d91..27dd75a8b55 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);
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@codeweavers.com --- dlls/dinput/Makefile.in | 3 +- dlls/dinput/dinput_main.c | 3 +- dlls/dinput/dinput_private.h | 1 + dlls/dinput/joystick_hid.c | 358 +++++++++++++++++++++++++++++++++++ dlls/dinput8/Makefile.in | 3 +- 5 files changed, 365 insertions(+), 3 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_hid.c b/dlls/dinput/joystick_hid.c new file mode 100644 index 00000000000..0d362cf9af3 --- /dev/null +++ b/dlls/dinput/joystick_hid.c @@ -0,0 +1,358 @@ +/* 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 ref; + + if (!(ref = IDirectInputDevice2WImpl_Release( iface ))) + { + HidD_FreePreparsedData( tmp.preparsed ); + CloseHandle( tmp.device ); + } + + return ref; +} + +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 DIERR_UNSUPPORTED; +} + +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 DIERR_UNSUPPORTED; +} + +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 DIERR_UNSUPPORTED; +} + +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 DIERR_UNSUPPORTED; +} + +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, + IDirectInputDevice2WImpl_EnumObjects, + IDirectInputDevice2WImpl_GetProperty, + IDirectInputDevice2WImpl_SetProperty, + IDirectInputDevice2WImpl_Acquire, + IDirectInputDevice2WImpl_Unacquire, + hid_joystick_GetDeviceState, + IDirectInputDevice2WImpl_GetDeviceData, + IDirectInputDevice2WImpl_SetDataFormat, + IDirectInputDevice2WImpl_SetEventNotification, + IDirectInputDevice2WImpl_SetCooperativeLevel, + IDirectInputDevice2WImpl_GetObjectInfo, + hid_joystick_GetDeviceInfo, + IDirectInputDevice2WImpl_RunControlPanel, + IDirectInputDevice2WImpl_Initialize, + /*** IDirectInputDevice2 methods ***/ + IDirectInputDevice2WImpl_CreateEffect, + IDirectInputDevice2WImpl_EnumEffects, + IDirectInputDevice2WImpl_GetEffectInfo, + IDirectInputDevice2WImpl_GetForceFeedbackState, + IDirectInputDevice2WImpl_SendForceFeedbackCommand, + IDirectInputDevice2WImpl_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( UINT32 handle, const WCHAR *path, HANDLE *device, + PHIDP_PREPARSED_DATA *preparsed, HIDD_ATTRIBUTES *attrs, + HIDP_CAPS *caps, DIDEVICEINSTANCEW *instance, DWORD version ) +{ + 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( "game usage page not implemented!\n" ); + if (caps->UsagePage == HID_USAGE_PAGE_SIMULATION) FIXME( "simulation usage page not implemented!\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; + + instance->guidInstance = hid_joystick_guid; + instance->guidInstance.Data1 ^= 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; + + *device = device_file; + *preparsed = preparsed_data; + return TRUE; + +failed: + CloseHandle( device_file ); + HidD_FreePreparsedData( preparsed_data ); + return FALSE; +} + +static HRESULT hid_joystick_device_open( int index, DIDEVICEINSTANCEW *filter, WCHAR *device_path, + HANDLE *device, PHIDP_PREPARSED_DATA *preparsed, + HIDD_ATTRIBUTES *attrs, HIDP_CAPS *caps, DWORD version ) +{ + 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 = {.cbSize = sizeof(iface)}; + SP_DEVINFO_DATA devinfo = {.cbSize = sizeof(devinfo)}; + DIDEVICEINSTANCEW instance = *filter; + UINT32 i = 0, handle; + HDEVINFO set; + DWORD type; + GUID hid; + + TRACE( "index %d, product %s, instance %s\n", index, debugstr_guid( &filter->guidProduct ), + debugstr_guid( &filter->guidInstance ) ); + + HidD_GetHidGuid( &hid ); + + 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 *)&handle, sizeof(handle), NULL, 0 ) || + type != DEVPROP_TYPE_UINT32) + continue; + if (!hid_joystick_device_try_open( handle, detail->DevicePath, device, preparsed, + attrs, caps, &instance, version )) + continue; + + /* enumerate device by GUID */ + if (index < 0 && IsEqualGUID( &filter->guidProduct, &instance.guidProduct )) break; + if (index < 0 && IsEqualGUID( &filter->guidInstance, &instance.guidInstance )) break; + + /* enumerate all devices */ + if (index >= 0 && !index--) 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 ); + *filter = instance; + return DI_OK; +} + +static HRESULT hid_joystick_enum_device( DWORD type, DWORD flags, DIDEVICEINSTANCEW *instance, + DWORD version, int index ) +{ + HIDD_ATTRIBUTES attrs = {.Size = 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, instance, device_path, &device, &preparsed, + &attrs, &caps, version ); + 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, const GUID *guid, IDirectInputDevice8W **out ) +{ + DIDEVICEINSTANCEW instance = + { + .dwSize = sizeof(instance), + .guidProduct = *guid, + .guidInstance = *guid + }; + HIDD_ATTRIBUTES attrs = {.Size = sizeof(attrs)}; + struct hid_joystick *impl = NULL; + WCHAR device_path[MAX_PATH]; + HIDP_CAPS caps; + HRESULT hr; + + TRACE( "dinput %p, guid %s, out %p\n", dinput, debugstr_guid( guid ), out ); + + *out = NULL; + instance.guidProduct.Data1 = DInput_PIDVID_Product_GUID.Data1; + instance.guidInstance.Data1 = hid_joystick_guid.Data1; + if (IsEqualGUID( &DInput_PIDVID_Product_GUID, &instance.guidProduct )) + instance.guidProduct = *guid; + else if (IsEqualGUID( &hid_joystick_guid, &instance.guidInstance )) + instance.guidInstance = *guid; + else + return DIERR_DEVICENOTREG; + + hr = direct_input_device_alloc( sizeof(struct hid_joystick), &hid_joystick_vtbl, guid, + dinput, (void **)&impl ); + if (FAILED(hr)) return hr; + impl->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": hid_joystick.base.crit"); + impl->base.dwCoopLevel = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND; + + hr = hid_joystick_device_open( -1, &instance, device_path, &impl->device, &impl->preparsed, + &attrs, &caps, dinput->dwVersion ); + if (hr != DI_OK) goto failed; + + *out = &impl->base.IDirectInputDevice8W_iface; + return DI_OK; + +failed: + IDirectInputDevice_Release( &impl->base.IDirectInputDevice8W_iface ); + 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/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 \