From 1760ad9f1cd6b27f9fa1ba290042f0ceedc65860 Mon Sep 17 00:00:00 2001 From: Tk-Glitch Date: Sat, 30 Mar 2019 07:20:46 +0100 Subject: [PATCH] proton-tkg: Add SDL joystick support patchset --- proton-tkg/README.md | 2 - proton-tkg/proton-tkg.cfg | 3 + wine-tkg-git/PKGBUILD | 8 +- .../wine-tkg-patches/proton-sdl-joy.patch | 2588 +++++++++++++++++ 4 files changed, 2598 insertions(+), 3 deletions(-) create mode 100644 wine-tkg-git/wine-tkg-patches/proton-sdl-joy.patch diff --git a/proton-tkg/README.md b/proton-tkg/README.md index c554c627..530669e1 100644 --- a/proton-tkg/README.md +++ b/proton-tkg/README.md @@ -50,8 +50,6 @@ You can also change their default values before building in your `proton-tkg.cfg - SteamVR support is missing for compatibility reasons. -- Dinput SDL support is missing for lazyness reasons. - - In the userpatches folder, you'll find three patches I decided against merging in the master patch for proton-tkg. You can put them in wine-tkg-git userpatches dir if you want to use them. They might not apply cleanly on older wine bases. - Proton-tkg builds will get installed in `~/.steam/root/compatibilitytools.d` directory. If you want to uninstall a build, just delete its folder there and restart Steam. **MAKE SURE NO STEAM GAME IS CURRENTLY SET TO USE THAT SPECIFIC VERSION BEFORE DELETION** diff --git a/proton-tkg/proton-tkg.cfg b/proton-tkg/proton-tkg.cfg index dbada6c9..f7965a4f 100644 --- a/proton-tkg/proton-tkg.cfg +++ b/proton-tkg/proton-tkg.cfg @@ -5,6 +5,9 @@ # PROTON-TKG OPTIONS +# Proton SDL Joystick support +_sdl_joy_support="true" + # Disable nvapi and nvapi64 - Common fix for various games _proton_nvapi_disable="true" diff --git a/wine-tkg-git/PKGBUILD b/wine-tkg-git/PKGBUILD index 7fc86e9c..16ee7f25 100644 --- a/wine-tkg-git/PKGBUILD +++ b/wine-tkg-git/PKGBUILD @@ -438,6 +438,7 @@ source=("$_winesrcdir"::"git://source.winehq.org/git/wine.git${_plain_commit}" 'high-core-count-fix.patch' # High core count setup fix for <=4.0 'msvcrt_nativebuiltin.patch' # Enforce mscvrt Dlls to native then builtin - from Proton 'nvidia-hate.patch' # novideo hate patch to disable nvapi, nvcuda etc. + 'proton-sdl-joy.patch' # Proton SDL joystick support ) if [ "$_EXTERNAL_INSTALL" == "true" ]; then @@ -1044,6 +1045,10 @@ prepare() { if [ "$_msvcrt_nativebuiltin" == "true" ]; then _patchname='msvcrt_nativebuiltin.patch' && _patchmsg="Enforce msvcrt Dlls to native then builtin (from Proton)" && nonuser_patcher fi + # SDL Joystick support - from Proton + if [ "$_sdl_joy_support" == "true" ]; then + _patchname='proton-sdl-joy.patch' && _patchmsg="Enable SDL Joystick support (from Proton)" && nonuser_patcher + fi fi # wine user patches @@ -1434,6 +1439,7 @@ md5sums=('SKIP' '498d9dea03eeedad973f7e33e11b48c0' 'd047619c11d95c4c9bbefae564876950' '22d3b0b1ff7f2104312b5eb29959cb2e' - 'bcf6dd7c16f37f20cca6ec4edfab5b34') + 'bcf6dd7c16f37f20cca6ec4edfab5b34' + '52f11ca72f4ee06bd2a4bc2b0314e631') trap exit_cleanup EXIT diff --git a/wine-tkg-git/wine-tkg-patches/proton-sdl-joy.patch b/wine-tkg-git/wine-tkg-patches/proton-sdl-joy.patch new file mode 100644 index 00000000..f834692e --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-sdl-joy.patch @@ -0,0 +1,2588 @@ +From 881abc8ac09f7076cdd765c04d6c1d05600296d9 Mon Sep 17 00:00:00 2001 +From: Aric Stewart +Date: Thu, 28 Dec 2017 12:43:32 -0600 +Subject: [PATCH] dinput: Add SDL support + +v2: Include comments from Andrew including opening and closing devices more cleanly +Signed-off-by: Aric Stewart +--- + dlls/dinput/Makefile.in | 4 +- + dlls/dinput/dinput_main.c | 21 ++ + dlls/dinput/dinput_private.h | 1 + + dlls/dinput/joystick_sdl.c | 707 +++++++++++++++++++++++++++++++++++ + dlls/dinput8/Makefile.in | 5 +- + 5 files changed, 736 insertions(+), 2 deletions(-) + create mode 100644 dlls/dinput/joystick_sdl.c + +diff --git a/dlls/dinput/Makefile.in b/dlls/dinput/Makefile.in +index 3b046d61d44..f968dbbcc32 100644 +--- a/dlls/dinput/Makefile.in ++++ b/dlls/dinput/Makefile.in +@@ -2,7 +2,8 @@ MODULE = dinput.dll + IMPORTLIB = dinput + IMPORTS = dxguid uuid comctl32 ole32 user32 advapi32 + EXTRADEFS = -DDIRECTINPUT_VERSION=0x0700 +-EXTRALIBS = $(IOKIT_LIBS) $(FORCEFEEDBACK_LIBS) ++EXTRALIBS = $(IOKIT_LIBS) $(FORCEFEEDBACK_LIBS) $(SDL2_LIBS) ++EXTRAINCL = $(SDL2_CFLAGS) + + C_SRCS = \ + config.c \ +@@ -14,6 +15,7 @@ C_SRCS = \ + joystick_linux.c \ + joystick_linuxinput.c \ + joystick_osx.c \ ++ joystick_sdl.c \ + keyboard.c \ + mouse.c + +diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c +index 54777da2d96..ad3619abcaf 100644 +--- a/dlls/dinput/dinput_main.c ++++ b/dlls/dinput/dinput_main.c +@@ -88,6 +88,7 @@ static const struct dinput_device *dinput_devices[] = + { + &mouse_device, + &keyboard_device, ++ &joystick_sdl_device, + &joystick_linuxinput_device, + &joystick_linux_device, + &joystick_osx_device +@@ -451,6 +452,7 @@ static HRESULT WINAPI IDirectInputAImpl_EnumDevices( + unsigned int i; + int j; + HRESULT r; ++ BOOL found_device = FALSE; + + TRACE("(this=%p,0x%04x '%s',%p,%p,0x%04x)\n", + This, dwDevType, _dump_DIDEVTYPE_value(dwDevType, This->dwVersion), +@@ -472,9 +474,15 @@ static HRESULT WINAPI IDirectInputAImpl_EnumDevices( + 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) ++ { ++ found_device = TRUE; + if (enum_callback_wrapper(lpCallback, &devInstance, pvRef) == DIENUM_STOP) + return S_OK; ++ } + } ++ /* If we have found devices after SDL stop enumeration */ ++ if (dinput_devices[i] == &joystick_sdl_device && found_device) ++ return S_OK; + } + + return S_OK; +@@ -491,6 +499,7 @@ static HRESULT WINAPI IDirectInputWImpl_EnumDevices( + unsigned int i; + int j; + HRESULT r; ++ BOOL found_device = FALSE; + + TRACE("(this=%p,0x%04x '%s',%p,%p,0x%04x)\n", + This, dwDevType, _dump_DIDEVTYPE_value(dwDevType, This->dwVersion), +@@ -512,9 +521,15 @@ static HRESULT WINAPI IDirectInputWImpl_EnumDevices( + TRACE(" - checking device %u ('%s')\n", i, dinput_devices[i]->name); + r = dinput_devices[i]->enum_deviceW(dwDevType, dwFlags, &devInstance, This->dwVersion, j); + if (r == S_OK) ++ { ++ found_device = TRUE; + if (enum_callback_wrapper(lpCallback, &devInstance, pvRef) == DIENUM_STOP) + return S_OK; ++ } + } ++ /* If we have found devices after SDL stop enumeration */ ++ if (dinput_devices[i] == &joystick_sdl_device && found_device) ++ return S_OK; + } + + return S_OK; +@@ -1084,6 +1099,9 @@ static HRESULT WINAPI IDirectInput8AImpl_EnumDevicesBySemantics( + didevis[device_count-1] = didevi; + } + } ++ /* If we have found devices after SDL stop enumeration */ ++ if (dinput_devices[i] == &joystick_sdl_device && device_count) ++ return S_OK; + } + + remain = device_count; +@@ -1189,6 +1207,9 @@ static HRESULT WINAPI IDirectInput8WImpl_EnumDevicesBySemantics( + didevis[device_count-1] = didevi; + } + } ++ /* If we have found devices after SDL stop enumeration */ ++ if (dinput_devices[i] == &joystick_sdl_device && device_count) ++ return S_OK; + } + + remain = device_count; +diff --git a/dlls/dinput/dinput_private.h b/dlls/dinput/dinput_private.h +index abdfbeb2531..1d87999ff50 100644 +--- a/dlls/dinput/dinput_private.h ++++ b/dlls/dinput/dinput_private.h +@@ -70,6 +70,7 @@ extern const struct dinput_device keyboard_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; ++extern const struct dinput_device joystick_sdl_device DECLSPEC_HIDDEN; + + extern void check_dinput_hooks(LPDIRECTINPUTDEVICE8W) DECLSPEC_HIDDEN; + extern void check_dinput_events(void) DECLSPEC_HIDDEN; +diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c +new file mode 100644 +index 00000000000..de54add9b6e +--- /dev/null ++++ b/dlls/dinput/joystick_sdl.c +@@ -0,0 +1,707 @@ ++/* DirectInput Joystick device from SDL ++ * ++ * Copyright 1998,2000 Marcus Meissner ++ * Copyright 1998,1999 Lionel Ulmer ++ * Copyright 2000-2001 TransGaming Technologies Inc. ++ * Copyright 2005 Daniel Remenak ++ * Copyright 2017 CodeWeavers, Aric Stewart ++ * ++ * 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 "config.h" ++#include "wine/port.h" ++ ++#include ++#include ++#include ++#include ++#include ++#ifdef HAVE_UNISTD_H ++# include ++#endif ++#ifdef HAVE_SDL2_SDL_H ++# include ++#endif ++#include ++ ++#include "wine/debug.h" ++#include "wine/unicode.h" ++#include "wine/list.h" ++#include "windef.h" ++#include "winbase.h" ++#include "winerror.h" ++#include "winreg.h" ++#include "dinput.h" ++ ++#include "dinput_private.h" ++#include "device_private.h" ++#include "joystick_private.h" ++ ++#ifdef HAVE_SDL2_SDL_H ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dinput); ++ ++typedef struct JoystickImpl JoystickImpl; ++static const IDirectInputDevice8AVtbl JoystickAvt; ++static const IDirectInputDevice8WVtbl JoystickWvt; ++ ++struct SDLDev { ++ int id; ++ WORD vendor_id; ++ WORD product_id; ++ CHAR *name; ++ ++ BOOL has_ff; ++}; ++ ++struct JoystickImpl ++{ ++ struct JoystickGenericImpl generic; ++ struct SDLDev *sdldev; ++ ++ SDL_Joystick *device; ++}; ++ ++static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface) ++{ ++ return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface), ++ JoystickGenericImpl, base), JoystickImpl, generic); ++} ++static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface) ++{ ++ return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface), ++ JoystickGenericImpl, base), JoystickImpl, generic); ++} ++ ++static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl(JoystickImpl *This) ++{ ++ return &This->generic.base.IDirectInputDevice8W_iface; ++} ++ ++static const GUID DInput_Wine_SDL_Joystick_GUID = { /* 001E36B7-5DBA-4C4F-A8C9-CFC8689DB403 */ ++ 0x001E36B7, 0x5DBA, 0x4C4F, {0xA8, 0xC9, 0xCF, 0xC8, 0x68, 0x9D, 0xB4, 0x03} ++}; ++ ++static int have_sdldevs = -1; ++static struct SDLDev *sdldevs = NULL; ++ ++static void find_sdldevs(void) ++{ ++ int i; ++ ++ if (InterlockedCompareExchange(&have_sdldevs, 0, -1) != -1) ++ /* Someone beat us to it */ ++ return; ++ ++ SDL_Init(SDL_INIT_JOYSTICK|SDL_INIT_HAPTIC); ++ SDL_JoystickEventState(SDL_ENABLE); ++ ++ for (i = 0; i < SDL_NumJoysticks(); i++) ++ { ++ struct SDLDev sdldev = {0}; ++ struct SDLDev *new_sdldevs; ++ SDL_Joystick *device; ++ const CHAR* name; ++ ++ sdldev.id = i; ++ device = SDL_JoystickOpen(i); ++ ++ name = SDL_JoystickName(device); ++ sdldev.name = HeapAlloc(GetProcessHeap(), 0, strlen(name) + 1); ++ strcpy(sdldev.name, name); ++ ++ if (device_disabled_registry(sdldev.name)) { ++ SDL_JoystickClose(device); ++ continue; ++ } ++ ++ TRACE("Found a joystick (%i) on %p: %s\n", have_sdldevs, device, sdldev.name); ++ ++ sdldev.vendor_id = SDL_JoystickGetVendor(device); ++ sdldev.product_id = SDL_JoystickGetProduct(device); ++ ++ if (!have_sdldevs) ++ new_sdldevs = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SDLDev)); ++ else ++ new_sdldevs = HeapReAlloc(GetProcessHeap(), 0, sdldevs, (1 + have_sdldevs) * sizeof(struct SDLDev)); ++ ++ SDL_JoystickClose(device); ++ if (!new_sdldevs) ++ { ++ continue; ++ } ++ sdldevs = new_sdldevs; ++ sdldevs[have_sdldevs] = sdldev; ++ have_sdldevs++; ++ } ++} ++ ++static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD version, int id) ++{ ++ DWORD dwSize = lpddi->dwSize; ++ ++ TRACE("%d %p\n", dwSize, lpddi); ++ memset(lpddi, 0, dwSize); ++ ++ lpddi->dwSize = dwSize; ++ lpddi->guidInstance = DInput_Wine_SDL_Joystick_GUID; ++ lpddi->guidInstance.Data3 = id; ++ lpddi->guidProduct = DInput_Wine_SDL_Joystick_GUID; ++ lpddi->guidFFDriver = GUID_NULL; ++ ++ if (version >= 0x0800) ++ lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8); ++ else ++ lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8); ++ ++ /* Assume the joystick as HID if it is attached to USB bus and has a valid VID/PID */ ++ if ( sdldevs[id].vendor_id && sdldevs[id].product_id) ++ { ++ lpddi->dwDevType |= DIDEVTYPE_HID; ++ lpddi->wUsagePage = 0x01; /* Desktop */ ++ if (lpddi->dwDevType == DI8DEVTYPE_JOYSTICK || lpddi->dwDevType == DIDEVTYPE_JOYSTICK) ++ lpddi->wUsage = 0x04; /* Joystick */ ++ else ++ lpddi->wUsage = 0x05; /* Game Pad */ ++ } ++ ++ MultiByteToWideChar(CP_ACP, 0, sdldevs[id].name, -1, lpddi->tszInstanceName, MAX_PATH); ++ MultiByteToWideChar(CP_ACP, 0, sdldevs[id].name, -1, lpddi->tszProductName, MAX_PATH); ++} ++ ++static void fill_joystick_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD version, int id) ++{ ++ DIDEVICEINSTANCEW lpddiW; ++ DWORD dwSize = lpddi->dwSize; ++ ++ lpddiW.dwSize = sizeof(lpddiW); ++ fill_joystick_dideviceinstanceW(&lpddiW, version, id); ++ ++ TRACE("%d %p\n", dwSize, lpddi); ++ memset(lpddi, 0, dwSize); ++ ++ /* Convert W->A */ ++ lpddi->dwSize = dwSize; ++ lpddi->guidInstance = lpddiW.guidInstance; ++ lpddi->guidProduct = lpddiW.guidProduct; ++ lpddi->dwDevType = lpddiW.dwDevType; ++ strcpy(lpddi->tszInstanceName, sdldevs[id].name); ++ strcpy(lpddi->tszProductName, sdldevs[id].name); ++ lpddi->guidFFDriver = lpddiW.guidFFDriver; ++ lpddi->wUsagePage = lpddiW.wUsagePage; ++ lpddi->wUsage = lpddiW.wUsage; ++} ++ ++static HRESULT sdl_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id) ++{ ++ find_sdldevs(); ++ ++ if (id >= have_sdldevs) { ++ return E_FAIL; ++ } ++ ++ if (!((dwDevType == 0) || ++ ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) || ++ (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800)))) ++ return S_FALSE; ++ ++ if (!(dwFlags & DIEDFL_FORCEFEEDBACK) || sdldevs[id].has_ff) { ++ fill_joystick_dideviceinstanceA(lpddi, version, id); ++ return S_OK; ++ } ++ return S_FALSE; ++} ++ ++static HRESULT sdl_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id) ++{ ++ find_sdldevs(); ++ ++ if (id >= have_sdldevs) { ++ return E_FAIL; ++ } ++ ++ if (!((dwDevType == 0) || ++ ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) || ++ (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800)))) ++ return S_FALSE; ++ ++ if (!(dwFlags & DIEDFL_FORCEFEEDBACK) || sdldevs[id].has_ff) { ++ fill_joystick_dideviceinstanceW(lpddi, version, id); ++ return S_OK; ++ } ++ return S_FALSE; ++} ++ ++static void poll_sdl_device_state(LPDIRECTINPUTDEVICE8A iface) ++{ ++ JoystickImpl *This = impl_from_IDirectInputDevice8A(iface); ++ int i; ++ int inst_id = 0; ++ int newVal = 0; ++ ++ SDL_JoystickUpdate(); ++ ++ for (i = 0; i < SDL_JoystickNumButtons(This->device); i++) ++ { ++ int val = SDL_JoystickGetButton(This->device, i); ++ int oldVal = This->generic.js.rgbButtons[i]; ++ newVal = val ? 0x80 : 0x0; ++ This->generic.js.rgbButtons[i] = newVal; ++ if (oldVal != newVal) ++ { ++ TRACE("Button: %i val %d oldVal %d newVal %d\n", i, val, oldVal, newVal); ++ inst_id = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON; ++ queue_event(iface, inst_id, newVal, GetCurrentTime(), This->generic.base.dinput->evsequence++); ++ } ++ } ++ for (i = 0; i < SDL_JoystickNumAxes(This->device); i++) ++ { ++ int oldVal; ++ newVal = SDL_JoystickGetAxis(This->device, i); ++ newVal = joystick_map_axis(&This->generic.props[i], newVal); ++ switch (i) ++ { ++ case 0: oldVal = This->generic.js.lX; ++ This->generic.js.lX = newVal; break; ++ case 1: oldVal = This->generic.js.lY; ++ This->generic.js.lY = newVal; break; ++ case 2: oldVal = This->generic.js.lZ; ++ This->generic.js.lZ = newVal; break; ++ case 3: oldVal = This->generic.js.lRx; ++ This->generic.js.lRx = newVal; break; ++ case 4: oldVal = This->generic.js.lRy; ++ This->generic.js.lRy = newVal; break; ++ case 5: oldVal = This->generic.js.lRz; ++ This->generic.js.lRz = newVal; break; ++ case 6: oldVal = This->generic.js.rglSlider[0]; ++ This->generic.js.rglSlider[0] = newVal; break; ++ case 7: oldVal = This->generic.js.rglSlider[1]; ++ This->generic.js.rglSlider[1] = newVal; break; ++ } ++ if (oldVal != newVal) ++ { ++ TRACE("Axis: %i oldVal %d newVal %d\n", i, oldVal, newVal); ++ inst_id = DIDFT_MAKEINSTANCE(i) | DIDFT_ABSAXIS; ++ queue_event(iface, inst_id, newVal, GetCurrentTime(), This->generic.base.dinput->evsequence++); ++ } ++ } ++ for (i = 0; i < SDL_JoystickNumHats(This->device); i++) ++ { ++ int oldVal = This->generic.js.rgdwPOV[i]; ++ newVal = SDL_JoystickGetHat(This->device, i); ++ switch (newVal) ++ { ++ case SDL_HAT_CENTERED: newVal = -1; break; ++ case SDL_HAT_UP: newVal = 0; break; ++ case SDL_HAT_RIGHTUP:newVal = 4500; break; ++ case SDL_HAT_RIGHT: newVal = 9000; break; ++ case SDL_HAT_RIGHTDOWN: newVal = 13500; break; ++ case SDL_HAT_DOWN: newVal = 18000; break; ++ case SDL_HAT_LEFTDOWN: newVal = 22500; break; ++ case SDL_HAT_LEFT: newVal = 27000; break; ++ case SDL_HAT_LEFTUP: newVal = 31500; break; ++ } ++ if (oldVal != newVal) ++ { ++ TRACE("Hat : %i oldVal %d newVal %d\n", i, oldVal, newVal); ++ This->generic.js.rgdwPOV[i] = newVal; ++ inst_id = DIDFT_MAKEINSTANCE(i) | DIDFT_POV; ++ queue_event(iface, inst_id, newVal, GetCurrentTime(), This->generic.base.dinput->evsequence++); ++ } ++ } ++} ++ ++static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsigned short index) ++{ ++ JoystickImpl* newDevice; ++ LPDIDATAFORMAT df = NULL; ++ DIDEVICEINSTANCEW ddi; ++ int i,idx = 0; ++ ++ newDevice = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JoystickImpl)); ++ if (!newDevice) return NULL; ++ ++ newDevice->generic.guidInstance = DInput_Wine_SDL_Joystick_GUID; ++ newDevice->generic.guidInstance.Data3 = index; ++ newDevice->generic.guidProduct = DInput_Wine_SDL_Joystick_GUID; ++ newDevice->generic.joy_polldev = poll_sdl_device_state; ++ ++ newDevice->generic.base.IDirectInputDevice8A_iface.lpVtbl = &JoystickAvt; ++ newDevice->generic.base.IDirectInputDevice8W_iface.lpVtbl = &JoystickWvt; ++ newDevice->generic.base.ref = 1; ++ newDevice->generic.base.guid = *rguid; ++ newDevice->generic.base.dinput = dinput; ++ newDevice->sdldev = &sdldevs[index]; ++ newDevice->generic.name = (char*)newDevice->sdldev->name; ++ ++ InitializeCriticalSection(&newDevice->generic.base.crit); ++ newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->base.crit"); ++ ++ /* Open Device */ ++ newDevice->device = SDL_JoystickOpen(newDevice->sdldev->id); ++ ++ /* Count number of available axes - supported Axis & POVs */ ++ newDevice->generic.devcaps.dwAxes = SDL_JoystickNumAxes(newDevice->device); ++ ++ if (newDevice->generic.devcaps.dwAxes > 8 ) ++ { ++ WARN("Can't support %d axis. Clamping down to 8\n", newDevice->generic.devcaps.dwAxes); ++ newDevice->generic.devcaps.dwAxes = 8; ++ } ++ ++ for (i = 0; i < newDevice->generic.devcaps.dwAxes; i++) ++ { ++ newDevice->generic.props[i].lDevMin = -32768; ++ newDevice->generic.props[i].lDevMax = 32767; ++ newDevice->generic.props[i].lMin = 0; ++ newDevice->generic.props[i].lMax = 0xffff; ++ newDevice->generic.props[i].lDeadZone = 0; ++ newDevice->generic.props[i].lSaturation = 0; ++ } ++ ++ newDevice->generic.devcaps.dwPOVs = SDL_JoystickNumHats(newDevice->device); ++ if (newDevice->generic.devcaps.dwPOVs > 4) ++ { ++ WARN("Can't support %d POV. Clamping down to 4\n", newDevice->generic.devcaps.dwPOVs); ++ newDevice->generic.devcaps.dwPOVs = 4; ++ } ++ ++ newDevice->generic.devcaps.dwButtons = SDL_JoystickNumButtons(newDevice->device); ++ if (newDevice->generic.devcaps.dwButtons > 128) ++ { ++ WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->generic.devcaps.dwButtons); ++ newDevice->generic.devcaps.dwButtons = 128; ++ } ++ ++ TRACE("axes %u povs %u buttons %u\n", newDevice->generic.devcaps.dwAxes, newDevice->generic.devcaps.dwPOVs, newDevice->generic.devcaps.dwButtons); ++ ++ /* 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; ++ if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto failed; ++ ++ for (i = 0; i < newDevice->generic.devcaps.dwAxes; i++) ++ { ++ memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[idx], df->dwObjSize); ++ df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(idx) | DIDFT_ABSAXIS; ++ ++idx; ++ } ++ ++ for (i = 0; i < newDevice->generic.devcaps.dwPOVs; i++) ++ { ++ memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 8], df->dwObjSize); ++ df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_POV; ++ } ++ ++ for (i = 0; i < newDevice->generic.devcaps.dwButtons; i++) ++ { ++ memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize); ++ df->rgodf[idx].pguid = &GUID_Button; ++ df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON; ++ } ++ newDevice->generic.base.data_format.wine_df = df; ++ ++ /* Fill the caps */ ++ newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps); ++ newDevice->generic.devcaps.dwFlags = DIDC_ATTACHED; ++ ++ ddi.dwSize = sizeof(ddi); ++ fill_joystick_dideviceinstanceW(&ddi, newDevice->generic.base.dinput->dwVersion, index); ++ newDevice->generic.devcaps.dwDevType = ddi.dwDevType; ++ ++ if (newDevice->sdldev->has_ff) ++ newDevice->generic.devcaps.dwFlags |= DIDC_FORCEFEEDBACK; ++ ++ IDirectInput_AddRef(&newDevice->generic.base.dinput->IDirectInput7A_iface); ++ ++ EnterCriticalSection(&dinput->crit); ++ list_add_tail(&dinput->devices_list, &newDevice->generic.base.entry); ++ LeaveCriticalSection(&dinput->crit); ++ ++ return newDevice; ++ ++failed: ++ if (df) HeapFree(GetProcessHeap(), 0, df->rgodf); ++ HeapFree(GetProcessHeap(), 0, df); ++ HeapFree(GetProcessHeap(), 0, newDevice); ++ return NULL; ++} ++ ++/****************************************************************************** ++ * get_joystick_index : Get the joystick index from a given GUID ++ */ ++static unsigned short get_joystick_index(REFGUID guid) ++{ ++ GUID wine_joystick = DInput_Wine_SDL_Joystick_GUID; ++ GUID dev_guid = *guid; ++ ++ wine_joystick.Data3 = 0; ++ dev_guid.Data3 = 0; ++ ++ /* for the standard joystick GUID use index 0 */ ++ if(IsEqualGUID(&GUID_Joystick,guid)) return 0; ++ ++ /* for the wine joystick GUIDs use the index stored in Data3 */ ++ if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3; ++ ++ return 0xffff; ++} ++ ++static HRESULT sdl_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPVOID *pdev, int unicode) ++{ ++ unsigned short index; ++ ++ TRACE("%p %s %s %p %i\n", dinput, debugstr_guid(rguid), debugstr_guid(riid), pdev, unicode); ++ ++ find_sdldevs(); ++ *pdev = NULL; ++ ++ if ((index = get_joystick_index(rguid)) < 0xffff && ++ have_sdldevs && index < have_sdldevs) ++ { ++ JoystickImpl *This; ++ ++ if (riid == NULL) ++ ;/* nothing */ ++ else if (IsEqualGUID(&IID_IDirectInputDeviceA, riid) || ++ IsEqualGUID(&IID_IDirectInputDevice2A, riid) || ++ IsEqualGUID(&IID_IDirectInputDevice7A, riid) || ++ IsEqualGUID(&IID_IDirectInputDevice8A, riid)) ++ { ++ unicode = 0; ++ } ++ else if (IsEqualGUID(&IID_IDirectInputDeviceW, riid) || ++ IsEqualGUID(&IID_IDirectInputDevice2W, riid) || ++ IsEqualGUID(&IID_IDirectInputDevice7W, riid) || ++ IsEqualGUID(&IID_IDirectInputDevice8W, riid)) ++ { ++ unicode = 1; ++ } ++ else ++ { ++ WARN("no interface\n"); ++ return DIERR_NOINTERFACE; ++ } ++ ++ This = alloc_device(rguid, dinput, index); ++ TRACE("Created a Joystick device (%p)\n", This); ++ ++ if (!This) return DIERR_OUTOFMEMORY; ++ ++ if (unicode) ++ *pdev = &This->generic.base.IDirectInputDevice8W_iface; ++ else ++ *pdev = &This->generic.base.IDirectInputDevice8A_iface; ++ ++ return DI_OK; ++ } ++ ++ return DIERR_DEVICENOTREG; ++} ++ ++const struct dinput_device joystick_sdl_device = { ++ "Wine SDL joystick driver", ++ sdl_enum_deviceA, ++ sdl_enum_deviceW, ++ sdl_create_device ++}; ++ ++static ULONG WINAPI JoystickWImpl_Release(LPDIRECTINPUTDEVICE8W iface) ++{ ++ JoystickImpl *This = impl_from_IDirectInputDevice8W(iface); ++ TRACE("(this=%p)\n", iface); ++ if (This->generic.base.ref == 1 && This->device >= 0) ++ { ++ TRACE("Closing Joystick: %p\n",This); ++ SDL_JoystickClose(This->device); ++ This->device = NULL; ++ } ++ return IDirectInputDevice2WImpl_Release(iface); ++} ++ ++static ULONG WINAPI JoystickAImpl_Release(LPDIRECTINPUTDEVICE8A iface) ++{ ++ JoystickImpl *This = impl_from_IDirectInputDevice8A(iface); ++ return JoystickWImpl_Release(IDirectInputDevice8W_from_impl(This)); ++} ++ ++/****************************************************************************** ++ * GetProperty : get input device properties ++ */ ++static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph) ++{ ++ JoystickImpl *This = impl_from_IDirectInputDevice8W(iface); ++ ++ TRACE("(this=%p,%s,%p)\n", iface, debugstr_guid(rguid), pdiph); ++ _dump_DIPROPHEADER(pdiph); ++ ++ if (!IS_DIPROP(rguid)) return DI_OK; ++ ++ switch (LOWORD(rguid)) { ++ ++ case (DWORD_PTR) DIPROP_VIDPID: ++ { ++ LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph; ++ ++ if (!This->sdldev->product_id || !This->sdldev->vendor_id) ++ return DIERR_UNSUPPORTED; ++ pd->dwData = MAKELONG(This->sdldev->vendor_id, This->sdldev->product_id); ++ TRACE("DIPROP_VIDPID(%08x)\n", pd->dwData); ++ break; ++ } ++ case (DWORD_PTR) DIPROP_JOYSTICKID: ++ { ++ LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph; ++ ++ pd->dwData = This->sdldev->id; ++ TRACE("DIPROP_JOYSTICKID(%d)\n", pd->dwData); ++ 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); ++} ++ ++/****************************************************************************** ++ * GetDeviceInfo : get information about a device's identity ++ */ ++static HRESULT WINAPI JoystickAImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8A iface, ++ LPDIDEVICEINSTANCEA pdidi) ++{ ++ JoystickImpl *This = impl_from_IDirectInputDevice8A(iface); ++ ++ TRACE("(%p) %p\n", This, pdidi); ++ ++ if (pdidi == NULL) return E_POINTER; ++ if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3A)) && ++ (pdidi->dwSize != sizeof(DIDEVICEINSTANCEA))) ++ return DIERR_INVALIDPARAM; ++ ++ fill_joystick_dideviceinstanceA(pdidi, This->generic.base.dinput->dwVersion, ++ get_joystick_index(&This->generic.base.guid)); ++ return DI_OK; ++} ++ ++static HRESULT WINAPI JoystickWImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8W iface, ++ LPDIDEVICEINSTANCEW pdidi) ++{ ++ JoystickImpl *This = impl_from_IDirectInputDevice8W(iface); ++ ++ TRACE("(%p) %p\n", This, pdidi); ++ ++ if (pdidi == NULL) return E_POINTER; ++ if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3W)) && ++ (pdidi->dwSize != sizeof(DIDEVICEINSTANCEW))) ++ return DIERR_INVALIDPARAM; ++ ++ fill_joystick_dideviceinstanceW(pdidi, This->generic.base.dinput->dwVersion, ++ get_joystick_index(&This->generic.base.guid)); ++ return DI_OK; ++} ++ ++static const IDirectInputDevice8AVtbl JoystickAvt = ++{ ++ IDirectInputDevice2AImpl_QueryInterface, ++ IDirectInputDevice2AImpl_AddRef, ++ JoystickAImpl_Release, ++ JoystickAGenericImpl_GetCapabilities, ++ IDirectInputDevice2AImpl_EnumObjects, ++ JoystickAImpl_GetProperty, ++ JoystickAGenericImpl_SetProperty, ++ IDirectInputDevice2AImpl_Acquire, ++ IDirectInputDevice2AImpl_Unacquire, ++ JoystickAGenericImpl_GetDeviceState, ++ IDirectInputDevice2AImpl_GetDeviceData, ++ IDirectInputDevice2AImpl_SetDataFormat, ++ IDirectInputDevice2AImpl_SetEventNotification, ++ IDirectInputDevice2AImpl_SetCooperativeLevel, ++ JoystickAGenericImpl_GetObjectInfo, ++ JoystickAImpl_GetDeviceInfo, ++ IDirectInputDevice2AImpl_RunControlPanel, ++ IDirectInputDevice2AImpl_Initialize, ++ IDirectInputDevice2AImpl_CreateEffect, ++ IDirectInputDevice2AImpl_EnumEffects, ++ IDirectInputDevice2AImpl_GetEffectInfo, ++ IDirectInputDevice2AImpl_GetForceFeedbackState, ++ IDirectInputDevice2AImpl_SendForceFeedbackCommand, ++ IDirectInputDevice2AImpl_EnumCreatedEffectObjects, ++ IDirectInputDevice2AImpl_Escape, ++ JoystickAGenericImpl_Poll, ++ IDirectInputDevice2AImpl_SendDeviceData, ++ IDirectInputDevice7AImpl_EnumEffectsInFile, ++ IDirectInputDevice7AImpl_WriteEffectToFile, ++ JoystickAGenericImpl_BuildActionMap, ++ JoystickAGenericImpl_SetActionMap, ++ IDirectInputDevice8AImpl_GetImageInfo ++}; ++ ++static const IDirectInputDevice8WVtbl JoystickWvt = ++{ ++ IDirectInputDevice2WImpl_QueryInterface, ++ IDirectInputDevice2WImpl_AddRef, ++ JoystickWImpl_Release, ++ JoystickWGenericImpl_GetCapabilities, ++ IDirectInputDevice2WImpl_EnumObjects, ++ JoystickWImpl_GetProperty, ++ JoystickWGenericImpl_SetProperty, ++ IDirectInputDevice2WImpl_Acquire, ++ IDirectInputDevice2WImpl_Unacquire, ++ JoystickWGenericImpl_GetDeviceState, ++ IDirectInputDevice2WImpl_GetDeviceData, ++ IDirectInputDevice2WImpl_SetDataFormat, ++ IDirectInputDevice2WImpl_SetEventNotification, ++ IDirectInputDevice2WImpl_SetCooperativeLevel, ++ JoystickWGenericImpl_GetObjectInfo, ++ JoystickWImpl_GetDeviceInfo, ++ IDirectInputDevice2WImpl_RunControlPanel, ++ IDirectInputDevice2WImpl_Initialize, ++ IDirectInputDevice2WImpl_CreateEffect, ++ IDirectInputDevice2WImpl_EnumEffects, ++ IDirectInputDevice2WImpl_GetEffectInfo, ++ IDirectInputDevice2WImpl_GetForceFeedbackState, ++ IDirectInputDevice2WImpl_SendForceFeedbackCommand, ++ IDirectInputDevice2WImpl_EnumCreatedEffectObjects, ++ IDirectInputDevice2WImpl_Escape, ++ JoystickWGenericImpl_Poll, ++ IDirectInputDevice2WImpl_SendDeviceData, ++ IDirectInputDevice7WImpl_EnumEffectsInFile, ++ IDirectInputDevice7WImpl_WriteEffectToFile, ++ JoystickWGenericImpl_BuildActionMap, ++ JoystickWGenericImpl_SetActionMap, ++ IDirectInputDevice8WImpl_GetImageInfo ++}; ++ ++#else ++ ++const struct dinput_device joystick_sdl_device = { ++ "Wine SDL joystick driver", ++ NULL, ++ NULL, ++ NULL ++}; ++ ++#endif +diff --git a/dlls/dinput8/Makefile.in b/dlls/dinput8/Makefile.in +index 0ede9f0fb16..9735fb55465 100644 +--- a/dlls/dinput8/Makefile.in ++++ b/dlls/dinput8/Makefile.in +@@ -2,7 +2,8 @@ MODULE = dinput8.dll + IMPORTLIB = dinput8 + IMPORTS = dxguid uuid comctl32 ole32 user32 advapi32 + EXTRADEFS = -DDIRECTINPUT_VERSION=0x0800 +-EXTRALIBS = $(IOKIT_LIBS) $(FORCEFEEDBACK_LIBS) ++EXTRALIBS = $(IOKIT_LIBS) $(FORCEFEEDBACK_LIBS) $(SDL2_LIBS) ++EXTRAINCL = $(SDL2_CFLAGS) + PARENTSRC = ../dinput + + C_SRCS = \ +@@ -11,9 +12,11 @@ C_SRCS = \ + device.c \ + dinput_main.c \ + effect_linuxinput.c \ ++ effect_sdl.c \ + joystick.c \ + joystick_linux.c \ + joystick_linuxinput.c \ ++ joystick_sdl.c \ + joystick_osx.c \ + keyboard.c \ + mouse.c +From 69287b9bd2fb18738811fc2e4fa988ada69de10c Mon Sep 17 00:00:00 2001 +From: Aric Stewart +Date: Thu, 28 Dec 2017 13:44:29 -0600 +Subject: [PATCH] dinput: Begin SDL haptic implemeting Get/SetProperty + +Signed-off-by: Aric Stewart +--- + dlls/dinput/joystick_sdl.c | 106 ++++++++++++++++++++++++++++++++++++- + 1 file changed, 104 insertions(+), 2 deletions(-) + +diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c +index de54add9b6e..48608015105 100644 +--- a/dlls/dinput/joystick_sdl.c ++++ b/dlls/dinput/joystick_sdl.c +@@ -65,6 +65,8 @@ struct SDLDev { + CHAR *name; + + BOOL has_ff; ++ int autocenter; ++ int gain; + }; + + struct JoystickImpl +@@ -73,6 +75,7 @@ struct JoystickImpl + struct SDLDev *sdldev; + + SDL_Joystick *device; ++ SDL_Haptic *haptic; + }; + + static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface) +@@ -130,6 +133,17 @@ static void find_sdldevs(void) + + TRACE("Found a joystick (%i) on %p: %s\n", have_sdldevs, device, sdldev.name); + ++ if (SDL_JoystickIsHaptic(device)) ++ { ++ SDL_Haptic *haptic = SDL_HapticOpenFromJoystick(device); ++ if (haptic) ++ { ++ TRACE(" ... with force feedback\n"); ++ sdldev.has_ff = TRUE; ++ SDL_HapticClose(haptic); ++ } ++ } ++ + sdldev.vendor_id = SDL_JoystickGetVendor(device); + sdldev.product_id = SDL_JoystickGetProduct(device); + +@@ -352,6 +366,7 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig + + /* Open Device */ + newDevice->device = SDL_JoystickOpen(newDevice->sdldev->id); ++ newDevice->haptic = SDL_HapticOpenFromJoystick(newDevice->device); + + /* Count number of available axes - supported Axis & POVs */ + newDevice->generic.devcaps.dwAxes = SDL_JoystickNumAxes(newDevice->device); +@@ -399,6 +414,8 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig + { + memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[idx], df->dwObjSize); + df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(idx) | DIDFT_ABSAXIS; ++ if (newDevice->sdldev->has_ff && i < 2) ++ df->rgodf[idx].dwFlags |= DIDOI_FFACTUATOR; + ++idx; + } + +@@ -414,6 +431,10 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig + df->rgodf[idx].pguid = &GUID_Button; + df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON; + } ++ ++ if (newDevice->sdldev->has_ff) ++ newDevice->generic.devcaps.dwFlags |= DIDC_FORCEFEEDBACK; ++ + newDevice->generic.base.data_format.wine_df = df; + + /* Fill the caps */ +@@ -528,6 +549,8 @@ static ULONG WINAPI JoystickWImpl_Release(LPDIRECTINPUTDEVICE8W iface) + if (This->generic.base.ref == 1 && This->device >= 0) + { + TRACE("Closing Joystick: %p\n",This); ++ if (This->sdldev->has_ff) ++ SDL_HapticClose(This->haptic); + SDL_JoystickClose(This->device); + This->device = NULL; + } +@@ -553,7 +576,22 @@ static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REF + if (!IS_DIPROP(rguid)) return DI_OK; + + switch (LOWORD(rguid)) { ++ case (DWORD_PTR) DIPROP_AUTOCENTER: ++ { ++ LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph; + ++ pd->dwData = This->sdldev->autocenter ? DIPROPAUTOCENTER_ON : DIPROPAUTOCENTER_OFF; ++ TRACE("autocenter(%d)\n", pd->dwData); ++ break; ++ } ++ case (DWORD_PTR) DIPROP_FFGAIN: ++ { ++ LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph; ++ ++ pd->dwData = This->sdldev->gain; ++ TRACE("DIPROP_FFGAIN(%d)\n", pd->dwData); ++ break; ++ } + case (DWORD_PTR) DIPROP_VIDPID: + { + LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph; +@@ -586,6 +624,70 @@ static HRESULT WINAPI JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface, REF + return JoystickWImpl_GetProperty(IDirectInputDevice8W_from_impl(This), rguid, pdiph); + } + ++static BOOL _SetProperty(JoystickImpl *This, const GUID *prop, const DIPROPHEADER *header) ++{ ++ int rc; ++ ++ switch(LOWORD(prop)) ++ { ++ case (DWORD_PTR)DIPROP_AUTOCENTER: ++ { ++ LPCDIPROPDWORD pd = (LPCDIPROPDWORD)header; ++ ++ This->sdldev->autocenter = pd->dwData == DIPROPAUTOCENTER_ON; ++ ++ rc = SDL_HapticSetAutocenter(This->haptic, This->sdldev->autocenter * 100); ++ if (rc != 0) ++ ERR("SDL_HapticSetAutocenter failed: %s\n", SDL_GetError()); ++ break; ++ } ++ case (DWORD_PTR)DIPROP_FFGAIN: ++ { ++ LPCDIPROPDWORD pd = (LPCDIPROPDWORD)header; ++ int sdl_gain = MulDiv(This->sdldev->gain, 100, 10000); ++ ++ TRACE("DIPROP_FFGAIN(%d)\n", pd->dwData); ++ ++ This->sdldev->gain = pd->dwData; ++ ++ rc = SDL_HapticSetGain(This->haptic, sdl_gain); ++ if (rc != 0) ++ ERR("SDL_HapticSetGain (%i -> %i) failed: %s\n", pd->dwData, sdl_gain, SDL_GetError()); ++ break; ++ } ++ default: ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static HRESULT WINAPI JoystickWImpl_SetProperty(IDirectInputDevice8W *iface, ++ const GUID *prop, const DIPROPHEADER *header) ++{ ++ JoystickImpl *This = impl_from_IDirectInputDevice8W(iface); ++ ++ TRACE("%p %s %p\n", This, debugstr_guid(prop), header); ++ ++ if (_SetProperty(This, prop, header)) ++ return DI_OK; ++ else ++ return JoystickWGenericImpl_SetProperty(iface, prop, header); ++} ++ ++static HRESULT WINAPI JoystickAImpl_SetProperty(IDirectInputDevice8A *iface, ++ const GUID *prop, const DIPROPHEADER *header) ++{ ++ JoystickImpl *This = impl_from_IDirectInputDevice8A(iface); ++ ++ TRACE("%p %s %p\n", This, debugstr_guid(prop), header); ++ ++ if (_SetProperty(This, prop, header)) ++ return DI_OK; ++ else ++ return JoystickAGenericImpl_SetProperty(iface, prop, header); ++} ++ + /****************************************************************************** + * GetDeviceInfo : get information about a device's identity + */ +@@ -631,7 +733,7 @@ static const IDirectInputDevice8AVtbl JoystickAvt = + JoystickAGenericImpl_GetCapabilities, + IDirectInputDevice2AImpl_EnumObjects, + JoystickAImpl_GetProperty, +- JoystickAGenericImpl_SetProperty, ++ JoystickAImpl_SetProperty, + IDirectInputDevice2AImpl_Acquire, + IDirectInputDevice2AImpl_Unacquire, + JoystickAGenericImpl_GetDeviceState, +@@ -667,7 +769,7 @@ static const IDirectInputDevice8WVtbl JoystickWvt = + JoystickWGenericImpl_GetCapabilities, + IDirectInputDevice2WImpl_EnumObjects, + JoystickWImpl_GetProperty, +- JoystickWGenericImpl_SetProperty, ++ JoystickWImpl_SetProperty, + IDirectInputDevice2WImpl_Acquire, + IDirectInputDevice2WImpl_Unacquire, + JoystickWGenericImpl_GetDeviceState, +From e00826b6efa849c61f802f51ba4ab250c53111bf Mon Sep 17 00:00:00 2001 +From: Aric Stewart +Date: Wed, 3 Jan 2018 12:50:21 -0600 +Subject: [PATCH] dinput: Implement GetEffectInfo, SendForceFeedbackCommand and + EnumCreatedEffectObjects + +Signed-off-by: Aric Stewart +--- + dlls/dinput/joystick_sdl.c | 259 ++++++++++++++++++++++++++++++++++++- + 1 file changed, 253 insertions(+), 6 deletions(-) + +diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c +index 48608015105..2b1c9176242 100644 +--- a/dlls/dinput/joystick_sdl.c ++++ b/dlls/dinput/joystick_sdl.c +@@ -67,6 +67,7 @@ struct SDLDev { + BOOL has_ff; + int autocenter; + int gain; ++ struct list effects; + }; + + struct JoystickImpl +@@ -360,6 +361,9 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig + newDevice->generic.base.dinput = dinput; + newDevice->sdldev = &sdldevs[index]; + newDevice->generic.name = (char*)newDevice->sdldev->name; ++ list_init(&newDevice->sdldev->effects); ++ newDevice->sdldev->autocenter = 1; ++ newDevice->sdldev->gain = 100; + + InitializeCriticalSection(&newDevice->generic.base.crit); + newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->base.crit"); +@@ -725,6 +729,249 @@ static HRESULT WINAPI JoystickWImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8W iface, + return DI_OK; + } + ++static HRESULT WINAPI JoystickWImpl_EnumEffects(LPDIRECTINPUTDEVICE8W iface, ++ LPDIENUMEFFECTSCALLBACKW lpCallback, ++ LPVOID pvRef, ++ DWORD dwEffType) ++{ ++ DIEFFECTINFOW dei; ++ DWORD type = DIEFT_GETTYPE(dwEffType); ++ JoystickImpl* This = impl_from_IDirectInputDevice8W(iface); ++ unsigned int query; ++ ++ TRACE("(this=%p,%p,%d) type=%d\n", This, pvRef, dwEffType, type); ++ ++ dei.dwSize = sizeof(DIEFFECTINFOW); ++ query = SDL_HapticQuery(This->haptic); ++ TRACE("Effects 0x%x\n",query); ++ ++ if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE) ++ && (query & SDL_HAPTIC_CONSTANT)) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce); ++ (*lpCallback)(&dei, pvRef); ++ } ++ ++ if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE) && ++ (query & SDL_HAPTIC_RAMP)) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce); ++ (*lpCallback)(&dei, pvRef); ++ } ++ ++ if (type == DIEFT_ALL || type == DIEFT_PERIODIC) ++ { ++ if (query & SDL_HAPTIC_SINE) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine); ++ (*lpCallback)(&dei, pvRef); ++ } ++ if (query & SDL_HAPTIC_TRIANGLE) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle); ++ (*lpCallback)(&dei, pvRef); ++ } ++ if (query & SDL_HAPTIC_SAWTOOTHUP) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp); ++ (*lpCallback)(&dei, pvRef); ++ } ++ if (query & SDL_HAPTIC_SAWTOOTHDOWN) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown); ++ (*lpCallback)(&dei, pvRef); ++ } ++ } ++ ++ if (type == DIEFT_ALL || type == DIEFT_CONDITION) ++ { ++ if (query & SDL_HAPTIC_SPRING) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring); ++ (*lpCallback)(&dei, pvRef); ++ } ++ if (query & SDL_HAPTIC_DAMPER) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper); ++ (*lpCallback)(&dei, pvRef); ++ } ++ if (query & SDL_HAPTIC_INERTIA) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia); ++ (*lpCallback)(&dei, pvRef); ++ } ++ if (query & SDL_HAPTIC_FRICTION) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction); ++ (*lpCallback)(&dei, pvRef); ++ } ++ } ++ ++ return DI_OK; ++} ++ ++static HRESULT WINAPI JoystickAImpl_EnumEffects(LPDIRECTINPUTDEVICE8A iface, ++ LPDIENUMEFFECTSCALLBACKA lpCallback, ++ LPVOID pvRef, ++ DWORD dwEffType) ++{ ++ DIEFFECTINFOA dei; ++ DWORD type = DIEFT_GETTYPE(dwEffType); ++ JoystickImpl* This = impl_from_IDirectInputDevice8A(iface); ++ unsigned int query; ++ ++ TRACE("(this=%p,%p,%d) type=%d\n", This, pvRef, dwEffType, type); ++ ++ dei.dwSize = sizeof(DIEFFECTINFOA); ++ query = SDL_HapticQuery(This->haptic); ++ TRACE("Effects 0x%x\n",query); ++ ++ if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE) ++ && (query & SDL_HAPTIC_CONSTANT)) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce); ++ (*lpCallback)(&dei, pvRef); ++ } ++ ++ if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE) && ++ (query & SDL_HAPTIC_RAMP)) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce); ++ (*lpCallback)(&dei, pvRef); ++ } ++ ++ if (type == DIEFT_ALL || type == DIEFT_PERIODIC) ++ { ++ if (query & SDL_HAPTIC_SINE) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine); ++ (*lpCallback)(&dei, pvRef); ++ } ++ if (query & SDL_HAPTIC_TRIANGLE) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle); ++ (*lpCallback)(&dei, pvRef); ++ } ++ if (query & SDL_HAPTIC_SAWTOOTHUP) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp); ++ (*lpCallback)(&dei, pvRef); ++ } ++ if (query & SDL_HAPTIC_SAWTOOTHDOWN) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown); ++ (*lpCallback)(&dei, pvRef); ++ } ++ } ++ ++ if (type == DIEFT_ALL || type == DIEFT_CONDITION) ++ { ++ if (query & SDL_HAPTIC_SPRING) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring); ++ (*lpCallback)(&dei, pvRef); ++ } ++ if (query & SDL_HAPTIC_DAMPER) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper); ++ (*lpCallback)(&dei, pvRef); ++ } ++ if (query & SDL_HAPTIC_INERTIA) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia); ++ (*lpCallback)(&dei, pvRef); ++ } ++ if (query & SDL_HAPTIC_FRICTION) ++ { ++ IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction); ++ (*lpCallback)(&dei, pvRef); ++ } ++ } ++ ++ return DI_OK; ++} ++ ++static HRESULT WINAPI JoystickWImpl_SendForceFeedbackCommand(LPDIRECTINPUTDEVICE8W iface, DWORD dwFlags) ++{ ++ JoystickImpl* This = impl_from_IDirectInputDevice8W(iface); ++ TRACE("(this=%p,%d)\n", This, dwFlags); ++ ++ switch (dwFlags) ++ { ++ case DISFFC_STOPALL: ++ { ++ effect_list_item *itr; ++ ++ /* Stop all effects */ ++ LIST_FOR_EACH_ENTRY(itr, &This->sdldev->effects, effect_list_item, entry) ++ IDirectInputEffect_Stop(itr->ref); ++ break; ++ } ++ ++ case DISFFC_RESET: ++ { ++ effect_list_item *itr; ++ ++ /* Stop and unload all effects. It is not true that effects are released */ ++ LIST_FOR_EACH_ENTRY(itr, &This->sdldev->effects, effect_list_item, entry) ++ { ++ IDirectInputEffect_Stop(itr->ref); ++ IDirectInputEffect_Unload(itr->ref); ++ } ++ break; ++ } ++ case DISFFC_PAUSE: ++ case DISFFC_CONTINUE: ++ FIXME("No support for Pause or Continue in sdl\n"); ++ break; ++ ++ case DISFFC_SETACTUATORSOFF: ++ case DISFFC_SETACTUATORSON: ++ FIXME("No direct actuator control in sdl\n"); ++ break; ++ ++ default: ++ WARN("Unknown Force Feedback Command %u!\n", dwFlags); ++ return DIERR_INVALIDPARAM; ++ } ++ return DI_OK; ++} ++ ++static HRESULT WINAPI JoystickAImpl_SendForceFeedbackCommand(LPDIRECTINPUTDEVICE8A iface, DWORD dwFlags) ++{ ++ JoystickImpl *This = impl_from_IDirectInputDevice8A(iface); ++ return JoystickWImpl_SendForceFeedbackCommand(IDirectInputDevice8W_from_impl(This), dwFlags); ++} ++ ++static HRESULT WINAPI JoystickWImpl_EnumCreatedEffectObjects(LPDIRECTINPUTDEVICE8W iface, ++ LPDIENUMCREATEDEFFECTOBJECTSCALLBACK lpCallback, ++ LPVOID pvRef, DWORD dwFlags) ++{ ++ JoystickImpl* This = impl_from_IDirectInputDevice8W(iface); ++ effect_list_item *itr, *ptr; ++ ++ TRACE("(this=%p,%p,%p,%d)\n", This, lpCallback, pvRef, dwFlags); ++ ++ if (!lpCallback) ++ return DIERR_INVALIDPARAM; ++ ++ if (dwFlags != 0) ++ FIXME("Flags specified, but no flags exist yet (DX9)!\n"); ++ ++ LIST_FOR_EACH_ENTRY_SAFE(itr, ptr, &This->sdldev->effects, effect_list_item, entry) ++ (*lpCallback)(itr->ref, pvRef); ++ ++ return DI_OK; ++} ++ ++static HRESULT WINAPI JoystickAImpl_EnumCreatedEffectObjects(LPDIRECTINPUTDEVICE8A iface, ++ LPDIENUMCREATEDEFFECTOBJECTSCALLBACK lpCallback, ++ LPVOID pvRef, DWORD dwFlags) ++{ ++ JoystickImpl *This = impl_from_IDirectInputDevice8A(iface); ++ return JoystickWImpl_EnumCreatedEffectObjects(IDirectInputDevice8W_from_impl(This), lpCallback, pvRef, dwFlags); ++} ++ + static const IDirectInputDevice8AVtbl JoystickAvt = + { + IDirectInputDevice2AImpl_QueryInterface, +@@ -746,11 +993,11 @@ static const IDirectInputDevice8AVtbl JoystickAvt = + IDirectInputDevice2AImpl_RunControlPanel, + IDirectInputDevice2AImpl_Initialize, + IDirectInputDevice2AImpl_CreateEffect, +- IDirectInputDevice2AImpl_EnumEffects, ++ JoystickAImpl_EnumEffects, + IDirectInputDevice2AImpl_GetEffectInfo, + IDirectInputDevice2AImpl_GetForceFeedbackState, +- IDirectInputDevice2AImpl_SendForceFeedbackCommand, +- IDirectInputDevice2AImpl_EnumCreatedEffectObjects, ++ JoystickAImpl_SendForceFeedbackCommand, ++ JoystickAImpl_EnumCreatedEffectObjects, + IDirectInputDevice2AImpl_Escape, + JoystickAGenericImpl_Poll, + IDirectInputDevice2AImpl_SendDeviceData, +@@ -782,11 +1029,11 @@ static const IDirectInputDevice8WVtbl JoystickWvt = + IDirectInputDevice2WImpl_RunControlPanel, + IDirectInputDevice2WImpl_Initialize, + IDirectInputDevice2WImpl_CreateEffect, +- IDirectInputDevice2WImpl_EnumEffects, ++ JoystickWImpl_EnumEffects, + IDirectInputDevice2WImpl_GetEffectInfo, + IDirectInputDevice2WImpl_GetForceFeedbackState, +- IDirectInputDevice2WImpl_SendForceFeedbackCommand, +- IDirectInputDevice2WImpl_EnumCreatedEffectObjects, ++ JoystickWImpl_SendForceFeedbackCommand, ++ JoystickWImpl_EnumCreatedEffectObjects, + IDirectInputDevice2WImpl_Escape, + JoystickWGenericImpl_Poll, + IDirectInputDevice2WImpl_SendDeviceData, +From 1c01af59094cb675890e7736c84087db516dd48b Mon Sep 17 00:00:00 2001 +From: Aric Stewart +Date: Wed, 3 Jan 2018 13:26:27 -0600 +Subject: [PATCH] dinput: Implement FF effects for SDL joysticks + +Signed-off-by: Aric Stewart +--- + dlls/dinput/Makefile.in | 1 + + dlls/dinput/effect_sdl.c | 848 +++++++++++++++++++++++++++++++++++++ + dlls/dinput/joystick_sdl.c | 93 +++- + 3 files changed, 938 insertions(+), 4 deletions(-) + create mode 100644 dlls/dinput/effect_sdl.c + +diff --git a/dlls/dinput/Makefile.in b/dlls/dinput/Makefile.in +index f968dbbcc32..a3e38816636 100644 +--- a/dlls/dinput/Makefile.in ++++ b/dlls/dinput/Makefile.in +@@ -11,6 +11,7 @@ C_SRCS = \ + device.c \ + dinput_main.c \ + effect_linuxinput.c \ ++ effect_sdl.c \ + joystick.c \ + joystick_linux.c \ + joystick_linuxinput.c \ +diff --git a/dlls/dinput/effect_sdl.c b/dlls/dinput/effect_sdl.c +new file mode 100644 +index 00000000000..8055785cc02 +--- /dev/null ++++ b/dlls/dinput/effect_sdl.c +@@ -0,0 +1,848 @@ ++/* DirectInput Joystick device from SDL ++ * ++ * Copyright 2017 CodeWeavers, Aric Stewart ++ * ++ * 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 "config.h" ++#include "wine/port.h" ++ ++#include ++#include ++#include ++#include ++#include ++#ifdef HAVE_UNISTD_H ++# include ++#endif ++#ifdef HAVE_SDL2_SDL_H ++# include ++#endif ++#include ++ ++#include "wine/debug.h" ++#include "wine/unicode.h" ++#include "wine/list.h" ++#include "windef.h" ++#include "winbase.h" ++#include "winerror.h" ++#include "winreg.h" ++#include "dinput.h" ++ ++#include "dinput_private.h" ++#include "device_private.h" ++#include "joystick_private.h" ++ ++#ifdef HAVE_SDL2_SDL_H ++ ++WINE_DEFAULT_DEBUG_CHANNEL(dinput); ++ ++typedef struct _SDLInputEffectImpl { ++ IDirectInputEffect IDirectInputEffect_iface; ++ LONG ref; ++ ++ SDL_Haptic *haptic; ++ GUID guid; ++ ++ SDL_HapticEffect effect; ++ int effect_id; ++ BOOL first_axis_is_x; ++ ++ struct list *entry; ++} SDLInputEffectImpl; ++ ++static SDLInputEffectImpl *impl_from_IDirectInputEffect(IDirectInputEffect *iface) ++{ ++ return CONTAINING_RECORD(iface, SDLInputEffectImpl, IDirectInputEffect_iface); ++} ++ ++static const IDirectInputEffectVtbl EffectVtbl; ++ ++ ++static HRESULT WINAPI effect_QueryInterface(IDirectInputEffect *iface, ++ const GUID *guid, void **out) ++{ ++ SDLInputEffectImpl *This = impl_from_IDirectInputEffect(iface); ++ ++ TRACE("%p %s %p\n", This, debugstr_guid(guid), out); ++ ++ if(IsEqualIID(guid, &IID_IUnknown) || IsEqualIID(guid, &IID_IDirectInputEffect)){ ++ *out = iface; ++ IDirectInputEffect_AddRef(iface); ++ return DI_OK; ++ } ++ ++ return E_NOINTERFACE; ++} ++ ++static ULONG WINAPI effect_AddRef(IDirectInputEffect *iface) ++{ ++ SDLInputEffectImpl *This = impl_from_IDirectInputEffect(iface); ++ ULONG ref = InterlockedIncrement(&This->ref); ++ TRACE("%p, ref is now: %u\n", This, ref); ++ return ref; ++} ++ ++static ULONG WINAPI effect_Release(IDirectInputEffect *iface) ++{ ++ SDLInputEffectImpl *This = impl_from_IDirectInputEffect(iface); ++ ULONG ref = InterlockedDecrement(&This->ref); ++ TRACE("%p, ref is now: %u\n", This, ref); ++ ++ if (!ref) ++ { ++ list_remove(This->entry); ++ if (This->effect_id >= 0) ++ SDL_HapticDestroyEffect(This->haptic, This->effect_id); ++ HeapFree(GetProcessHeap(), 0, LIST_ENTRY(This->entry, effect_list_item, entry)); ++ HeapFree(GetProcessHeap(), 0, This); ++ } ++ ++ return ref; ++} ++ ++static HRESULT WINAPI effect_Initialize(IDirectInputEffect *iface, HINSTANCE hinst, ++ DWORD version, const GUID *guid) ++{ ++ SDLInputEffectImpl *This = impl_from_IDirectInputEffect(iface); ++ TRACE("%p %p 0x%x, %s\n", This, hinst, version, debugstr_guid(guid)); ++ return DI_OK; ++} ++ ++static HRESULT WINAPI effect_GetEffectGuid(IDirectInputEffect *iface, GUID *out) ++{ ++ SDLInputEffectImpl *This = impl_from_IDirectInputEffect(iface); ++ TRACE("%p %p\n", This, out); ++ *out = This->guid; ++ return DI_OK; ++} ++ ++ ++#define GET_BASE_EFFECT_FIELD(target, field, value) {\ ++ if (target.type == SDL_HAPTIC_SINE || \ ++ target.type == SDL_HAPTIC_TRIANGLE || \ ++ target.type == SDL_HAPTIC_SAWTOOTHUP || \ ++ target.type == SDL_HAPTIC_SAWTOOTHDOWN) \ ++ value = (target.periodic.field); \ ++ else if (target.type == SDL_HAPTIC_CONSTANT) \ ++ value = (target.constant.field); \ ++ else if (target.type == SDL_HAPTIC_RAMP) \ ++ value = (target.ramp.field); \ ++ else if (target.type == SDL_HAPTIC_SPRING || \ ++ target.type == SDL_HAPTIC_DAMPER || \ ++ target.type == SDL_HAPTIC_INERTIA || \ ++ target.type == SDL_HAPTIC_FRICTION) \ ++ value = (target.condition.field); \ ++ else if (target.type == SDL_HAPTIC_CUSTOM) \ ++ value = (target.custom.field); \ ++ } ++ ++#define GET_EXTENDED_EFFECT_FIELD(target, field, value) {\ ++ if (target.type == SDL_HAPTIC_SINE || \ ++ target.type == SDL_HAPTIC_TRIANGLE || \ ++ target.type == SDL_HAPTIC_SAWTOOTHUP || \ ++ target.type == SDL_HAPTIC_SAWTOOTHDOWN) \ ++ value = (target.periodic.field); \ ++ else if (target.type == SDL_HAPTIC_CONSTANT) \ ++ value = (target.constant.field); \ ++ else if (target.type == SDL_HAPTIC_RAMP) \ ++ value = (target.ramp.field); \ ++ else if (target.type == SDL_HAPTIC_SPRING || \ ++ target.type == SDL_HAPTIC_DAMPER || \ ++ target.type == SDL_HAPTIC_INERTIA || \ ++ target.type == SDL_HAPTIC_FRICTION); \ ++ /* Ignored because extended fields are not preset in these effects */ \ ++ else if (target.type == SDL_HAPTIC_CUSTOM) \ ++ value = (target.custom.field); \ ++ } ++ ++#define SCALE(type, target_range, target_min, value, source_range, source_min) \ ++ (type)((((target_range)*(value + source_min))/source_range)-target_min) ++ ++static HRESULT WINAPI effect_GetParameters(IDirectInputEffect *iface, ++ DIEFFECT *effect, DWORD flags) ++{ ++ HRESULT hr = DI_OK; ++ SDLInputEffectImpl *This = impl_from_IDirectInputEffect(iface); ++ TRACE("%p %p 0x%x\n", This, effect, flags); ++ ++ if (flags & DIEP_AXES) ++ { ++ if (effect->cAxes < 2) ++ hr = DIERR_MOREDATA; ++ effect->cAxes = 2; ++ if (hr) ++ return hr; ++ else ++ { ++ effect->rgdwAxes[0] = DIJOFS_X; ++ effect->rgdwAxes[1] = DIJOFS_Y; ++ } ++ } ++ ++ if (flags & DIEP_DIRECTION) ++ { ++ if (effect->cAxes < 2) ++ hr = DIERR_MOREDATA; ++ effect->cAxes = 2; ++ if (hr) ++ return hr; ++ else ++ { ++ if (effect->dwFlags & DIEFF_CARTESIAN) ++ { ++ GET_BASE_EFFECT_FIELD(This->effect, direction.dir[0], effect->rglDirection[0]); ++ GET_BASE_EFFECT_FIELD(This->effect, direction.dir[1], effect->rglDirection[1]); ++ } ++ else ++ { ++ /* Polar and spherical coordinates */ ++ GET_BASE_EFFECT_FIELD(This->effect, direction.dir[0], effect->rglDirection[0]); ++ } ++ } ++ } ++ ++ if (flags & DIEP_DURATION) ++ { ++ int sdl_length = 0; ++ GET_BASE_EFFECT_FIELD(This->effect, length, sdl_length); ++ if (sdl_length == SDL_HAPTIC_INFINITY) ++ effect->dwDuration = INFINITE; ++ else ++ effect->dwDuration = (DWORD)sdl_length * 1000; ++ } ++ ++ if (flags & DIEP_ENVELOPE) ++ { ++ int sdl_attack_length = 0; ++ int sdl_attack_level = 0; ++ int sdl_fade_length = 0; ++ int sdl_fade_level = 0; ++ ++ GET_EXTENDED_EFFECT_FIELD(This->effect, attack_length, sdl_attack_length); ++ GET_EXTENDED_EFFECT_FIELD(This->effect, attack_level, sdl_attack_level); ++ GET_EXTENDED_EFFECT_FIELD(This->effect, fade_length, sdl_fade_length); ++ GET_EXTENDED_EFFECT_FIELD(This->effect, fade_level, sdl_fade_level); ++ ++ if (sdl_attack_length == 0 && sdl_attack_level == 0 && sdl_fade_length == 0 && sdl_fade_level == 0) ++ { ++ effect->lpEnvelope = NULL; ++ } ++ else if (effect->lpEnvelope == NULL) ++ { ++ return DIERR_INVALIDPARAM; ++ } ++ else ++ { ++ effect->lpEnvelope->dwAttackLevel = sdl_attack_level; ++ effect->lpEnvelope->dwAttackTime = sdl_attack_length * 1000; ++ effect->lpEnvelope->dwFadeLevel = sdl_fade_level; ++ effect->lpEnvelope->dwFadeTime = sdl_fade_length * 1000; ++ } ++ } ++ ++ if (flags & DIEP_GAIN) ++ effect->dwGain = 0; ++ ++ if (flags & DIEP_SAMPLEPERIOD) ++ effect->dwSamplePeriod = 0; ++ ++ if (flags & DIEP_STARTDELAY && effect->dwSize > sizeof(DIEFFECT_DX5)) ++ { ++ GET_BASE_EFFECT_FIELD(This->effect, delay, effect->dwStartDelay); ++ effect->dwStartDelay *= 1000; ++ } ++ ++ if (flags & DIEP_TRIGGERBUTTON) ++ { ++ int trigger = 0; ++ GET_BASE_EFFECT_FIELD(This->effect, button, trigger); ++ effect->dwTriggerButton = DIJOFS_BUTTON(trigger); ++ } ++ ++ if (flags & DIEP_TRIGGERREPEATINTERVAL) ++ { ++ GET_BASE_EFFECT_FIELD(This->effect, interval, effect->dwTriggerRepeatInterval); ++ effect->dwTriggerRepeatInterval *= 1000; ++ } ++ ++ if (flags & DIEP_TYPESPECIFICPARAMS) ++ { ++ if (This->effect.type == SDL_HAPTIC_SINE || ++ This->effect.type == SDL_HAPTIC_TRIANGLE || ++ This->effect.type == SDL_HAPTIC_SAWTOOTHUP || ++ This->effect.type == SDL_HAPTIC_SAWTOOTHDOWN) ++ { ++ DIPERIODIC *tsp = effect->lpvTypeSpecificParams; ++ ++ tsp->dwMagnitude = MulDiv(This->effect.periodic.magnitude, 10000, 32767); ++ tsp->lOffset = This->effect.periodic.offset; ++ tsp->dwPhase = This->effect.periodic.phase; ++ tsp->dwPeriod = This->effect.periodic.period * 1000; ++ } ++ else if (This->effect.type == SDL_HAPTIC_CONSTANT) ++ { ++ LPDICONSTANTFORCE tsp = effect->lpvTypeSpecificParams; ++ tsp->lMagnitude = SCALE(LONG, 20000, -10000, This->effect.constant.level, 0xffff, -32767); ++ } ++ else if (This->effect.type == SDL_HAPTIC_RAMP) ++ { ++ DIRAMPFORCE *tsp = effect->lpvTypeSpecificParams; ++ ++ tsp->lStart = SCALE(Sint16, 20000, -10000, This->effect.ramp.start, 0xffff, -32767); ++ tsp->lEnd = SCALE(Sint16, 20000, -10000, This->effect.ramp.end, 0xffff, -32767); ++ } ++ else if (This->effect.type == SDL_HAPTIC_SPRING || ++ This->effect.type == SDL_HAPTIC_DAMPER || ++ This->effect.type == SDL_HAPTIC_INERTIA || ++ This->effect.type == SDL_HAPTIC_FRICTION) ++ { ++ int i; ++ DICONDITION *tsp = effect->lpvTypeSpecificParams; ++ for (i = 0; i < 2; i++) ++ { ++ tsp[i].lOffset = SCALE(LONG, 20000, -10000, This->effect.condition.center[i], 0xffff, -32767); ++ tsp[i].lPositiveCoefficient = SCALE(LONG, 20000, -10000, This->effect.condition.right_coeff[i], 0xffff, -32767); ++ tsp[i].lNegativeCoefficient = SCALE(LONG, 10000, -20000, This->effect.condition.left_coeff[i], 0xffff, -32767); ++ tsp[i].dwPositiveSaturation = SCALE(DWORD, 10000, 0, This->effect.condition.right_sat[i], 0xffff, 0); ++ tsp[i].dwNegativeSaturation = SCALE(DWORD, 10000, 0, This->effect.condition.left_sat[i], 0xffff, 0); ++ tsp[i].lDeadBand = SCALE(LONG, 20000, -10000, This->effect.condition.deadband[i], 0xffff, -32767); ++ } ++ } ++ else if (This->effect.type == SDL_HAPTIC_CUSTOM) ++ { ++ DICUSTOMFORCE *tsp = effect->lpvTypeSpecificParams; ++ ++ tsp->cChannels = This->effect.custom.channels; ++ tsp->dwSamplePeriod = This->effect.custom.period * 1000; ++ tsp->cSamples = This->effect.custom.samples; ++ tsp->rglForceData = (LONG*)This->effect.custom.data; ++ } ++ } ++ ++ return hr; ++} ++ ++static HRESULT WINAPI effect_Download(IDirectInputEffect *iface) ++{ ++ SDLInputEffectImpl *This = impl_from_IDirectInputEffect(iface); ++ TRACE("%p\n", This); ++ ++ if (This->effect_id < 0) ++ { ++ This->effect_id = SDL_HapticNewEffect(This->haptic, &This->effect); ++ if(This->effect_id < 0) ++ { ++ ERR("SDL_HapticNewEffect failed (Effect type %i): %s\n", This->effect.type, SDL_GetError()); ++ return E_FAIL; ++ } ++ } ++ ++ return DI_OK; ++} ++ ++static HRESULT WINAPI effect_Start(IDirectInputEffect *iface, DWORD iterations, ++ DWORD flags) ++{ ++ SDLInputEffectImpl *This = impl_from_IDirectInputEffect(iface); ++ ++ TRACE("%p 0x%x 0x%x\n", This, iterations, flags); ++ ++ if (!(flags & DIES_NODOWNLOAD)) ++ { ++ if (This->effect_id == -1) ++ { ++ HRESULT res = effect_Download(iface); ++ if (res != DI_OK) ++ return res; ++ } ++ } ++ ++ if (iterations == INFINITE) iterations = SDL_HAPTIC_INFINITY; ++ ++ if (SDL_HapticRunEffect(This->haptic, This->effect_id, iterations) < 0) ++ { ++ ERR("SDL_HapticRunEffect failed: %s\n", SDL_GetError()); ++ return E_FAIL; ++ } ++ return DI_OK; ++} ++ ++#define SET_BASE_EFFECT_FIELD(target, field, value) {\ ++ if (target.type == SDL_HAPTIC_SINE || \ ++ target.type == SDL_HAPTIC_TRIANGLE || \ ++ target.type == SDL_HAPTIC_SAWTOOTHUP || \ ++ target.type == SDL_HAPTIC_SAWTOOTHDOWN) \ ++ (target.periodic.field) = value; \ ++ else if (target.type == SDL_HAPTIC_CONSTANT) \ ++ (target.constant.field) = value; \ ++ else if (target.type == SDL_HAPTIC_RAMP) \ ++ (target.ramp.field) = value; \ ++ else if (target.type == SDL_HAPTIC_SPRING || \ ++ target.type == SDL_HAPTIC_DAMPER || \ ++ target.type == SDL_HAPTIC_INERTIA || \ ++ target.type == SDL_HAPTIC_FRICTION) \ ++ (target.condition.field) = value; \ ++ else if (target.type == SDL_HAPTIC_CUSTOM) \ ++ (target.custom.field) = value; \ ++ } ++ ++#define SET_EXTENDED_EFFECT_FIELD(target, field, value) {\ ++ if (target.type == SDL_HAPTIC_SINE || \ ++ target.type == SDL_HAPTIC_TRIANGLE || \ ++ target.type == SDL_HAPTIC_SAWTOOTHUP || \ ++ target.type == SDL_HAPTIC_SAWTOOTHDOWN) \ ++ (target.periodic.field) = value; \ ++ else if (target.type == SDL_HAPTIC_CONSTANT) \ ++ (target.constant.field) = value; \ ++ else if (target.type == SDL_HAPTIC_RAMP) \ ++ (target.ramp.field) = value; \ ++ else if (target.type == SDL_HAPTIC_SPRING || \ ++ target.type == SDL_HAPTIC_DAMPER || \ ++ target.type == SDL_HAPTIC_INERTIA || \ ++ target.type == SDL_HAPTIC_FRICTION); \ ++ /* Ignored because extended fields are not preset in these effects */ \ ++ else if (target.type == SDL_HAPTIC_CUSTOM) \ ++ (target.custom.field) = value; \ ++ } ++ ++static HRESULT WINAPI effect_SetParameters(IDirectInputEffect *iface, ++ const DIEFFECT *effect, DWORD flags) ++{ ++ SDLInputEffectImpl *This = impl_from_IDirectInputEffect(iface); ++ HRESULT retval = DI_OK; ++ ++ TRACE("%p %p 0x%x\n", This, effect, flags); ++ ++ dump_DIEFFECT(effect, &This->guid, flags); ++ ++ if (IsEqualGUID(&This->guid, &GUID_Sine)) ++ This->effect.type = SDL_HAPTIC_SINE; ++ else if (IsEqualGUID(&This->guid, &GUID_Triangle)) ++ This->effect.type = SDL_HAPTIC_TRIANGLE; ++ else if (IsEqualGUID(&This->guid, &GUID_SawtoothUp)) ++ This->effect.type = SDL_HAPTIC_SAWTOOTHUP; ++ else if (IsEqualGUID(&This->guid, &GUID_SawtoothDown)) ++ This->effect.type = SDL_HAPTIC_SAWTOOTHDOWN; ++ else if (IsEqualGUID(&This->guid, &GUID_ConstantForce)) ++ This->effect.type = SDL_HAPTIC_CONSTANT; ++ else if (IsEqualGUID(&This->guid, &GUID_RampForce)) ++ This->effect.type = SDL_HAPTIC_RAMP; ++ else if (IsEqualGUID(&This->guid, &GUID_Spring)) ++ This->effect.type = SDL_HAPTIC_SPRING; ++ else if (IsEqualGUID(&This->guid, &GUID_Damper)) ++ This->effect.type = SDL_HAPTIC_DAMPER; ++ else if (IsEqualGUID(&This->guid, &GUID_Inertia)) ++ This->effect.type = SDL_HAPTIC_INERTIA; ++ else if (IsEqualGUID(&This->guid, &GUID_Friction)) ++ This->effect.type = SDL_HAPTIC_FRICTION; ++ else if (IsEqualGUID(&This->guid, &GUID_CustomForce)) ++ This->effect.type = SDL_HAPTIC_CUSTOM; ++ ++ if ((flags & ~DIEP_NORESTART & ~DIEP_NODOWNLOAD & ~DIEP_START) == 0) ++ { ++ /* set everything */ ++ flags = DIEP_AXES | DIEP_DIRECTION | DIEP_DURATION | DIEP_ENVELOPE | ++ DIEP_GAIN | DIEP_SAMPLEPERIOD | DIEP_STARTDELAY | DIEP_TRIGGERBUTTON | ++ DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS; ++ } ++ ++ if (flags & DIEP_AXES) ++ { ++ if (effect->cAxes > 2) ++ return DIERR_INVALIDPARAM; ++ else if (effect->cAxes < 1) ++ return DIERR_INCOMPLETEEFFECT; ++ This->first_axis_is_x = effect->rgdwAxes[0] == DIJOFS_X; ++ } ++ ++ if (flags & DIEP_DIRECTION) ++ { ++ if (effect->cAxes == 1) ++ { ++ if (effect->dwFlags & DIEFF_CARTESIAN) ++ { ++ SET_BASE_EFFECT_FIELD(This->effect, direction.type, SDL_HAPTIC_CARTESIAN); ++ if (flags & DIEP_AXES) ++ { ++ SET_BASE_EFFECT_FIELD(This->effect, direction.dir[0], effect->rglDirection[0]); ++ SET_BASE_EFFECT_FIELD(This->effect, direction.dir[1], effect->rglDirection[1]); ++ } ++ } else { ++ /* one-axis effects must use cartesian coords */ ++ return DIERR_INVALIDPARAM; ++ } ++ } ++ /* two axes */ ++ else ++ { ++ if (effect->dwFlags & DIEFF_CARTESIAN) ++ { ++ LONG x, y; ++ ++ SET_BASE_EFFECT_FIELD(This->effect, direction.type, SDL_HAPTIC_CARTESIAN); ++ ++ if (This->first_axis_is_x) ++ { ++ x = effect->rglDirection[0]; ++ y = effect->rglDirection[1]; ++ } ++ else ++ { ++ x = effect->rglDirection[1]; ++ y = effect->rglDirection[0]; ++ } ++ SET_BASE_EFFECT_FIELD(This->effect, direction.dir[0], x); ++ SET_BASE_EFFECT_FIELD(This->effect, direction.dir[1], y); ++ } ++ else ++ { ++ if (effect->dwFlags & DIEFF_POLAR) ++ { ++ SET_BASE_EFFECT_FIELD(This->effect, direction.type, SDL_HAPTIC_POLAR); ++ } ++ if (effect->dwFlags & DIEFF_SPHERICAL) ++ { ++ SET_BASE_EFFECT_FIELD(This->effect, direction.type, SDL_HAPTIC_SPHERICAL); ++ } ++ SET_BASE_EFFECT_FIELD(This->effect, direction.dir[0], effect->rglDirection[0]); ++ } ++ } ++ } ++ ++ if (flags & DIEP_DURATION) ++ { ++ if (effect->dwDuration == INFINITE) ++ { ++ SET_BASE_EFFECT_FIELD(This->effect, length, SDL_HAPTIC_INFINITY); ++ } ++ else if(effect->dwDuration > 1000) ++ { ++ SET_BASE_EFFECT_FIELD(This->effect, length, effect->dwDuration / 1000); ++ } ++ else ++ { ++ SET_BASE_EFFECT_FIELD(This->effect, length, 1); ++ } ++ } ++ ++ if (flags & DIEP_STARTDELAY && effect->dwSize > sizeof(DIEFFECT_DX5)) ++ { ++ SET_BASE_EFFECT_FIELD(This->effect, delay, effect->dwStartDelay / 1000); ++ } ++ ++ if (flags & DIEP_TRIGGERBUTTON) ++ { ++ SET_BASE_EFFECT_FIELD(This->effect, button, effect->dwTriggerButton); ++ } ++ ++ if (flags & DIEP_TRIGGERREPEATINTERVAL) ++ { ++ SET_BASE_EFFECT_FIELD(This->effect, interval, effect->dwTriggerRepeatInterval / 1000); ++ } ++ ++ if (flags & DIEP_TYPESPECIFICPARAMS) ++ { ++ if (IsEqualGUID(&This->guid, &GUID_Sine) || ++ IsEqualGUID(&This->guid, &GUID_Triangle) || ++ IsEqualGUID(&This->guid, &GUID_SawtoothUp) || ++ IsEqualGUID(&This->guid, &GUID_SawtoothDown)) ++ { ++ DIPERIODIC *tsp; ++ if (effect->cbTypeSpecificParams != sizeof(DIPERIODIC)) ++ return DIERR_INVALIDPARAM; ++ tsp = effect->lpvTypeSpecificParams; ++ ++ This->effect.periodic.magnitude = MulDiv(tsp->dwMagnitude, 32767, 10000); ++ This->effect.periodic.offset = tsp->lOffset; ++ This->effect.periodic.phase = tsp->dwPhase; ++ if (tsp->dwPeriod <= 1000) ++ This->effect.periodic.period = 1; ++ else ++ This->effect.periodic.period = tsp->dwPeriod / 1000; ++ } ++ else if (IsEqualGUID(&This->guid, &GUID_ConstantForce)) ++ { ++ DICONSTANTFORCE *tsp = effect->lpvTypeSpecificParams; ++ ++ if (effect->cbTypeSpecificParams != sizeof(DICONSTANTFORCE)) ++ return DIERR_INVALIDPARAM; ++ tsp = effect->lpvTypeSpecificParams; ++ This->effect.constant.level = SCALE(Sint16, 0xffff, -32767, tsp->lMagnitude, 20000, -10000); ++ } ++ else if (IsEqualGUID(&This->guid, &GUID_RampForce)) ++ { ++ DIRAMPFORCE *tsp = effect->lpvTypeSpecificParams; ++ ++ if (effect->cbTypeSpecificParams != sizeof(DIRAMPFORCE)) ++ return DIERR_INVALIDPARAM; ++ tsp = effect->lpvTypeSpecificParams; ++ This->effect.ramp.start = SCALE(Sint16, 0xffff, -32767, tsp->lStart, 20000, -10000); ++ This->effect.ramp.end = SCALE(Sint16, 0xffff, -32767, tsp->lEnd, 20000, -10000); ++ } ++ else if (IsEqualGUID(&This->guid, &GUID_Spring) || ++ IsEqualGUID(&This->guid, &GUID_Damper) || ++ IsEqualGUID(&This->guid, &GUID_Inertia) || ++ IsEqualGUID(&This->guid, &GUID_Friction)) ++ { ++ int sources; ++ int i,j; ++ DICONDITION *tsp = effect->lpvTypeSpecificParams; ++ ++ if (effect->cbTypeSpecificParams == sizeof(DICONDITION)) ++ sources = 1; ++ else if (effect->cbTypeSpecificParams == 2 * sizeof(DICONDITION)) ++ sources = 2; ++ else if (effect->cbTypeSpecificParams == 3 * sizeof(DICONDITION)) ++ sources = 3; ++ else ++ return DIERR_INVALIDPARAM; ++ ++ for (i = j = 0; i < 3; ++i) ++ { ++ This->effect.condition.right_sat[i] = SCALE(Uint16, 0xffff, 0, tsp[j].dwPositiveSaturation, 10000, 0); ++ This->effect.condition.left_sat[i] = SCALE(Uint16, 0xffff, 0, tsp[j].dwNegativeSaturation, 10000, 0); ++ This->effect.condition.right_coeff[i] = SCALE(Sint16, 0xffff, -32767, tsp[j].lPositiveCoefficient, 20000, -10000); ++ This->effect.condition.left_coeff[i] = SCALE(Sint16, 0xffff, -32767, tsp[j].lNegativeCoefficient, 20000, -10000); ++ This->effect.condition.deadband[i] = SCALE(Uint16, 0xffff, 0, tsp[j].lDeadBand, 10000, 0); ++ This->effect.condition.center[i] = SCALE(Sint16, 0xffff, -32767, tsp[j].lOffset, 20000, -10000); ++ if (sources-1 > j) ++ j++; ++ } ++ } ++ else if (IsEqualGUID(&This->guid, &GUID_CustomForce)) ++ { ++ DICUSTOMFORCE *tsp = effect->lpvTypeSpecificParams; ++ ++ if (effect->cbTypeSpecificParams != sizeof(DICUSTOMFORCE)) ++ return DIERR_INVALIDPARAM; ++ ++ This->effect.custom.channels = tsp->cChannels; ++ This->effect.custom.period = tsp->dwSamplePeriod / 1000; ++ This->effect.custom.samples = tsp->cSamples; ++ This->effect.custom.data = (Uint16*)tsp->rglForceData; ++ } ++ else ++ { ++ FIXME("Specific effect params for type %s no implemented.\n", debugstr_guid(&This->guid)); ++ } ++ } ++ ++ if (flags & DIEP_ENVELOPE) ++ { ++ if (effect->lpEnvelope) ++ { ++ SET_EXTENDED_EFFECT_FIELD(This->effect, attack_length, effect->lpEnvelope->dwAttackTime / 1000); ++ SET_EXTENDED_EFFECT_FIELD(This->effect, attack_level, effect->lpEnvelope->dwAttackLevel); ++ SET_EXTENDED_EFFECT_FIELD(This->effect, fade_length, effect->lpEnvelope->dwFadeTime / 1000); ++ SET_EXTENDED_EFFECT_FIELD(This->effect, fade_level, effect->lpEnvelope->dwFadeLevel); ++ } ++ else ++ { ++ SET_EXTENDED_EFFECT_FIELD(This->effect, attack_length, 0); ++ SET_EXTENDED_EFFECT_FIELD(This->effect, attack_level, 0); ++ SET_EXTENDED_EFFECT_FIELD(This->effect, fade_length, 0); ++ SET_EXTENDED_EFFECT_FIELD(This->effect, fade_level, 0); ++ } ++ } ++ ++ if (flags & DIEP_GAIN) ++ TRACE("Effect gain requested but no effect gain functionality present.\n"); ++ ++ if (flags & DIEP_SAMPLEPERIOD) ++ TRACE("Sample period requested but no sample period functionality present.\n"); ++ ++ if (This->effect_id >= 0) ++ { ++ if (SDL_HapticUpdateEffect(This->haptic, This->effect_id, &This->effect) < 0) ++ { ++ ERR("SDL_HapticUpdateEffect failed: %s\n",SDL_GetError()); ++ return E_FAIL; ++ } ++ ++ } ++ ++ if (!(flags & DIEP_NODOWNLOAD)) ++ retval = effect_Download(iface); ++ if (retval != DI_OK) ++ return DI_DOWNLOADSKIPPED; ++ ++ if (flags & DIEP_START) ++ retval = effect_Start(iface, 1, 0); ++ ++ return DI_OK; ++} ++ ++static HRESULT WINAPI effect_Stop(IDirectInputEffect *iface) ++{ ++ SDLInputEffectImpl *This = impl_from_IDirectInputEffect(iface); ++ TRACE("%p\n", This); ++ if (SDL_HapticStopEffect(This->haptic, This->effect_id) < 0) ++ { ++ ERR("SDL_HapticStopEffect failed: %s\n", SDL_GetError()); ++ return E_FAIL; ++ } ++ return DI_OK; ++} ++ ++static HRESULT WINAPI effect_GetEffectStatus(IDirectInputEffect *iface, DWORD *flags) ++{ ++ int rc; ++ SDLInputEffectImpl *This = impl_from_IDirectInputEffect(iface); ++ TRACE("%p %p %p %i\n", This, flags, This->haptic, This->effect_id); ++ ++ if (!flags) ++ return E_POINTER; ++ ++ if (This->effect_id == -1) ++ return DIERR_NOTDOWNLOADED; ++ ++ rc = SDL_HapticGetEffectStatus(This->haptic, This->effect_id); ++ switch (rc) ++ { ++ case 0: *flags = 0; break; ++ case 1: *flags = DIEGES_PLAYING; break; ++ default: ++ ERR("SDL_HapticGetEffectStatus failed: %s\n", SDL_GetError()); ++ } ++ return DI_OK; ++} ++ ++static HRESULT WINAPI effect_Unload(IDirectInputEffect *iface) ++{ ++ SDLInputEffectImpl *This = impl_from_IDirectInputEffect(iface); ++ TRACE("%p\n", This); ++ if (This->effect_id >= 0) ++ SDL_HapticDestroyEffect(This->haptic, This->effect_id); ++ This->effect_id = -1; ++ return DI_OK; ++} ++ ++static HRESULT WINAPI effect_Escape(IDirectInputEffect *iface, DIEFFESCAPE *escape) ++{ ++ SDLInputEffectImpl *This = impl_from_IDirectInputEffect(iface); ++ TRACE("%p %p\n", This, escape); ++ return E_NOTIMPL; ++} ++ ++static const IDirectInputEffectVtbl EffectVtbl = { ++ effect_QueryInterface, ++ effect_AddRef, ++ effect_Release, ++ effect_Initialize, ++ effect_GetEffectGuid, ++ effect_GetParameters, ++ effect_SetParameters, ++ effect_Start, ++ effect_Stop, ++ effect_GetEffectStatus, ++ effect_Download, ++ effect_Unload, ++ effect_Escape ++}; ++ ++/****************************************************************************** ++ * SDLInputEffect ++ */ ++ ++DECLSPEC_HIDDEN HRESULT sdl_create_effect(SDL_Haptic *device, REFGUID rguid, struct list *parent_list_entry, LPDIRECTINPUTEFFECT* peff) ++{ ++ SDLInputEffectImpl *effect; ++ ++ effect = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SDLInputEffectImpl)); ++ ++ effect->IDirectInputEffect_iface.lpVtbl = &EffectVtbl; ++ effect->ref = 1; ++ effect->guid = *rguid; ++ effect->haptic = device; ++ effect->effect_id = -1; ++ ++ effect->entry = parent_list_entry; ++ *peff = &effect->IDirectInputEffect_iface; ++ ++ return DI_OK; ++} ++ ++DECLSPEC_HIDDEN HRESULT sdl_input_get_info_A( ++ SDL_Joystick *dev, ++ REFGUID rguid, ++ LPDIEFFECTINFOA info) ++{ ++ DWORD type = typeFromGUID(rguid); ++ ++ TRACE("(%p, %s, %p) type=%d\n", dev, _dump_dinput_GUID(rguid), info, type); ++ ++ if (!info) return E_POINTER; ++ ++ if (info->dwSize != sizeof(DIEFFECTINFOA)) return DIERR_INVALIDPARAM; ++ ++ info->guid = *rguid; ++ ++ info->dwEffType = type; ++ /* the event device API does not support querying for all these things ++ * therefore we assume that we have support for them ++ * that's not as dangerous as it sounds, since drivers are allowed to ++ * ignore parameters they claim to support anyway */ ++ info->dwEffType |= DIEFT_DEADBAND | DIEFT_FFATTACK | DIEFT_FFFADE ++ | DIEFT_POSNEGCOEFFICIENTS | DIEFT_POSNEGSATURATION ++ | DIEFT_SATURATION | DIEFT_STARTDELAY; ++ ++ /* again, assume we have support for everything */ ++ info->dwStaticParams = DIEP_ALLPARAMS; ++ info->dwDynamicParams = info->dwStaticParams; ++ ++ /* yes, this is windows behavior (print the GUID_Name for name) */ ++ strcpy(info->tszName, _dump_dinput_GUID(rguid)); ++ ++ return DI_OK; ++} ++ ++DECLSPEC_HIDDEN HRESULT sdl_input_get_info_W( ++ SDL_Joystick *dev, ++ REFGUID rguid, ++ LPDIEFFECTINFOW info) ++{ ++ DWORD type = typeFromGUID(rguid); ++ ++ TRACE("(%p, %s, %p) type=%d\n", dev, _dump_dinput_GUID(rguid), info, type); ++ ++ if (!info) return E_POINTER; ++ ++ if (info->dwSize != sizeof(DIEFFECTINFOW)) return DIERR_INVALIDPARAM; ++ ++ info->guid = *rguid; ++ ++ info->dwEffType = type; ++ /* the event device API does not support querying for all these things ++ * therefore we assume that we have support for them ++ * that's not as dangerous as it sounds, since drivers are allowed to ++ * ignore parameters they claim to support anyway */ ++ info->dwEffType |= DIEFT_DEADBAND | DIEFT_FFATTACK | DIEFT_FFFADE ++ | DIEFT_POSNEGCOEFFICIENTS | DIEFT_POSNEGSATURATION ++ | DIEFT_SATURATION | DIEFT_STARTDELAY; ++ ++ /* again, assume we have support for everything */ ++ info->dwStaticParams = DIEP_ALLPARAMS; ++ info->dwDynamicParams = info->dwStaticParams; ++ ++ /* yes, this is windows behavior (print the GUID_Name for name) */ ++ MultiByteToWideChar(CP_ACP, 0, _dump_dinput_GUID(rguid), -1, ++ info->tszName, MAX_PATH); ++ ++ return DI_OK; ++} ++ ++#endif +diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c +index 2b1c9176242..64572f27142 100644 +--- a/dlls/dinput/joystick_sdl.c ++++ b/dlls/dinput/joystick_sdl.c +@@ -58,6 +58,12 @@ typedef struct JoystickImpl JoystickImpl; + static const IDirectInputDevice8AVtbl JoystickAvt; + static const IDirectInputDevice8WVtbl JoystickWvt; + ++/* implemented in effect_sdl.c */ ++HRESULT sdl_create_effect(SDL_Haptic *haptic, REFGUID rguid, struct list *parent_list_entry, LPDIRECTINPUTEFFECT* peff); ++HRESULT sdl_input_get_info_A(SDL_Joystick *dev, REFGUID rguid, LPDIEFFECTINFOA info); ++HRESULT sdl_input_get_info_W(SDL_Joystick *dev, REFGUID rguid, LPDIEFFECTINFOW info); ++ ++ + struct SDLDev { + int id; + WORD vendor_id; +@@ -729,6 +735,67 @@ static HRESULT WINAPI JoystickWImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8W iface, + return DI_OK; + } + ++static HRESULT WINAPI JoystickWImpl_CreateEffect(IDirectInputDevice8W *iface, ++ const GUID *rguid, const DIEFFECT *lpeff, IDirectInputEffect **ppdef, ++ IUnknown *pUnkOuter) ++{ ++ JoystickImpl *This = impl_from_IDirectInputDevice8W(iface); ++ HRESULT retval = DI_OK; ++ effect_list_item* new_effect = NULL; ++ ++ TRACE("%p %s %p %p %p\n", iface, debugstr_guid(rguid), lpeff, ppdef, pUnkOuter); ++ dump_DIEFFECT(lpeff, rguid, 0); ++ ++ if(!This->sdldev->has_ff){ ++ TRACE("No force feedback support\n"); ++ *ppdef = NULL; ++ return DIERR_UNSUPPORTED; ++ } ++ ++ if (pUnkOuter) ++ WARN("aggregation not implemented\n"); ++ ++ if (!(new_effect = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_effect)))) ++ return DIERR_OUTOFMEMORY; ++ ++ retval = sdl_create_effect(This->haptic, rguid, &new_effect->entry, &new_effect->ref); ++ if (retval != DI_OK) ++ { ++ HeapFree(GetProcessHeap(), 0, new_effect); ++ return retval; ++ } ++ ++ if (lpeff != NULL) ++ { ++ retval = IDirectInputEffect_SetParameters(new_effect->ref, lpeff, 0); ++ ++ if (retval != DI_OK && retval != DI_DOWNLOADSKIPPED) ++ { ++ HeapFree(GetProcessHeap(), 0, new_effect); ++ return retval; ++ } ++ } ++ ++ list_add_tail(&This->sdldev->effects, &new_effect->entry); ++ *ppdef = new_effect->ref; ++ ++ TRACE("allocated effect: %p\n", new_effect); ++ ++ return DI_OK; ++} ++ ++static HRESULT WINAPI JoystickAImpl_CreateEffect(IDirectInputDevice8A *iface, ++ const GUID *type, const DIEFFECT *params, IDirectInputEffect **out, ++ IUnknown *outer) ++{ ++ JoystickImpl *This = impl_from_IDirectInputDevice8A(iface); ++ ++ TRACE("%p %s %p %p %p\n", iface, debugstr_guid(type), params, out, outer); ++ ++ return JoystickWImpl_CreateEffect(&This->generic.base.IDirectInputDevice8W_iface, ++ type, params, out, outer); ++} ++ + static HRESULT WINAPI JoystickWImpl_EnumEffects(LPDIRECTINPUTDEVICE8W iface, + LPDIENUMEFFECTSCALLBACKW lpCallback, + LPVOID pvRef, +@@ -891,6 +958,24 @@ static HRESULT WINAPI JoystickAImpl_EnumEffects(LPDIRECTINPUTDEVICE8A iface, + return DI_OK; + } + ++static HRESULT WINAPI JoystickWImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8W iface, ++ LPDIEFFECTINFOW pdei, ++ REFGUID guid) ++{ ++ JoystickImpl* This = impl_from_IDirectInputDevice8W(iface); ++ TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid)); ++ return sdl_input_get_info_W(This->device, guid, pdei); ++} ++ ++static HRESULT WINAPI JoystickAImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8A iface, ++ LPDIEFFECTINFOA pdei, ++ REFGUID guid) ++{ ++ JoystickImpl* This = impl_from_IDirectInputDevice8A(iface); ++ TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid)); ++ return sdl_input_get_info_A(This->device, guid, pdei); ++} ++ + static HRESULT WINAPI JoystickWImpl_SendForceFeedbackCommand(LPDIRECTINPUTDEVICE8W iface, DWORD dwFlags) + { + JoystickImpl* This = impl_from_IDirectInputDevice8W(iface); +@@ -992,9 +1077,9 @@ static const IDirectInputDevice8AVtbl JoystickAvt = + JoystickAImpl_GetDeviceInfo, + IDirectInputDevice2AImpl_RunControlPanel, + IDirectInputDevice2AImpl_Initialize, +- IDirectInputDevice2AImpl_CreateEffect, ++ JoystickAImpl_CreateEffect, + JoystickAImpl_EnumEffects, +- IDirectInputDevice2AImpl_GetEffectInfo, ++ JoystickAImpl_GetEffectInfo, + IDirectInputDevice2AImpl_GetForceFeedbackState, + JoystickAImpl_SendForceFeedbackCommand, + JoystickAImpl_EnumCreatedEffectObjects, +@@ -1028,9 +1113,9 @@ static const IDirectInputDevice8WVtbl JoystickWvt = + JoystickWImpl_GetDeviceInfo, + IDirectInputDevice2WImpl_RunControlPanel, + IDirectInputDevice2WImpl_Initialize, +- IDirectInputDevice2WImpl_CreateEffect, ++ JoystickWImpl_CreateEffect, + JoystickWImpl_EnumEffects, +- IDirectInputDevice2WImpl_GetEffectInfo, ++ JoystickWImpl_GetEffectInfo, + IDirectInputDevice2WImpl_GetForceFeedbackState, + JoystickWImpl_SendForceFeedbackCommand, + JoystickWImpl_EnumCreatedEffectObjects, +From ff517137c35d61635b55209343545b9cc0a8b4d3 Mon Sep 17 00:00:00 2001 +From: Aric Stewart +Date: Mon, 8 Jan 2018 07:48:06 -0600 +Subject: [PATCH] dinput: implement DISFFC_PAUSE/DISFFC_CONTINUE for SDL + +Signed-off-by: Aric Stewart +--- + dlls/dinput/joystick_sdl.c | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c +index 64572f27142..a0073b2a9ed 100644 +--- a/dlls/dinput/joystick_sdl.c ++++ b/dlls/dinput/joystick_sdl.c +@@ -83,6 +83,7 @@ struct JoystickImpl + + SDL_Joystick *device; + SDL_Haptic *haptic; ++ BOOL ff_paused; + }; + + static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface) +@@ -755,6 +756,12 @@ static HRESULT WINAPI JoystickWImpl_CreateEffect(IDirectInputDevice8W *iface, + if (pUnkOuter) + WARN("aggregation not implemented\n"); + ++ if (This->ff_paused) ++ { ++ FIXME("Cannot add new effects to a paused SDL device\n"); ++ return DIERR_GENERIC; ++ } ++ + if (!(new_effect = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_effect)))) + return DIERR_OUTOFMEMORY; + +@@ -1006,8 +1013,14 @@ static HRESULT WINAPI JoystickWImpl_SendForceFeedbackCommand(LPDIRECTINPUTDEVICE + break; + } + case DISFFC_PAUSE: ++ This->ff_paused = TRUE; ++ if (SDL_HapticPause(This->haptic) != 0) ++ ERR("SDL_HapticPause failed: %s\n",SDL_GetError()); ++ break; + case DISFFC_CONTINUE: +- FIXME("No support for Pause or Continue in sdl\n"); ++ This->ff_paused = FALSE; ++ if (SDL_HapticUnpause(This->haptic) != 0) ++ ERR("SDL_HapticUnpause failed: %s\n",SDL_GetError()); + break; + + case DISFFC_SETACTUATORSOFF: +From 74a3817e14dd06d7677246d50b1062bf29c3fa2b Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Fri, 3 Aug 2018 12:37:34 -0500 +Subject: [PATCH] dinput: Use the VID/PID for the first chunk of the device + product GUID + +This is documented on MSDN: + + "XInput and DirectInput" + https://docs.microsoft.com/en-us/windows/desktop/xinput/xinput-and-directinput +--- + dlls/dinput/joystick_sdl.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c +index a0073b2a9ed..147e680d17a 100644 +--- a/dlls/dinput/joystick_sdl.c ++++ b/dlls/dinput/joystick_sdl.c +@@ -182,6 +182,7 @@ static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD ver + lpddi->guidInstance = DInput_Wine_SDL_Joystick_GUID; + lpddi->guidInstance.Data3 = id; + lpddi->guidProduct = DInput_Wine_SDL_Joystick_GUID; ++ lpddi->guidProduct.Data1 = MAKELONG(sdldevs[id].vendor_id, sdldevs[id].product_id); + lpddi->guidFFDriver = GUID_NULL; + + if (version >= 0x0800) +@@ -359,6 +360,7 @@ static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsig + newDevice->generic.guidInstance = DInput_Wine_SDL_Joystick_GUID; + newDevice->generic.guidInstance.Data3 = index; + newDevice->generic.guidProduct = DInput_Wine_SDL_Joystick_GUID; ++ newDevice->generic.guidProduct.Data1 = MAKELONG(sdldevs[index].vendor_id, sdldevs[index].product_id); + newDevice->generic.joy_polldev = poll_sdl_device_state; + + newDevice->generic.base.IDirectInputDevice8A_iface.lpVtbl = &JoystickAvt; +From d798c1d2714dc12f65e522c6ebfef2036294bc44 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 28 Aug 2018 10:35:08 -0500 +Subject: [PATCH] dinput: Don't fail to load on old SDL + +--- + dlls/dinput/joystick_sdl.c | 23 +++++++++++++++++++++-- + 1 file changed, 21 insertions(+), 2 deletions(-) + +diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c +index 147e680d17a..f7031524d13 100644 +--- a/dlls/dinput/joystick_sdl.c ++++ b/dlls/dinput/joystick_sdl.c +@@ -39,6 +39,7 @@ + + #include "wine/debug.h" + #include "wine/unicode.h" ++#include "wine/library.h" + #include "wine/list.h" + #include "windef.h" + #include "winbase.h" +@@ -112,6 +113,9 @@ static struct SDLDev *sdldevs = NULL; + static void find_sdldevs(void) + { + int i; ++ Uint16 (*pSDL_JoystickGetProduct)(SDL_Joystick * joystick) = NULL; ++ Uint16 (*pSDL_JoystickGetVendor)(SDL_Joystick * joystick) = NULL; ++ void *sdl_handle = NULL; + + if (InterlockedCompareExchange(&have_sdldevs, 0, -1) != -1) + /* Someone beat us to it */ +@@ -120,6 +124,16 @@ static void find_sdldevs(void) + SDL_Init(SDL_INIT_JOYSTICK|SDL_INIT_HAPTIC); + SDL_JoystickEventState(SDL_ENABLE); + ++ sdl_handle = wine_dlopen(SONAME_LIBSDL2, RTLD_NOW, NULL, 0); ++ if (sdl_handle) { ++ pSDL_JoystickGetProduct = wine_dlsym(sdl_handle, "SDL_JoystickGetProduct", NULL, 0); ++ pSDL_JoystickGetVendor = wine_dlsym(sdl_handle, "SDL_JoystickGetVendor", NULL, 0); ++ } ++ ++ if(!pSDL_JoystickGetVendor){ ++ ERR("SDL installation is old! Please upgrade to >=2.0.6 to get accurate joystick information.\n"); ++ } ++ + for (i = 0; i < SDL_NumJoysticks(); i++) + { + struct SDLDev sdldev = {0}; +@@ -152,8 +166,13 @@ static void find_sdldevs(void) + } + } + +- sdldev.vendor_id = SDL_JoystickGetVendor(device); +- sdldev.product_id = SDL_JoystickGetProduct(device); ++ if(pSDL_JoystickGetVendor){ ++ sdldev.vendor_id = pSDL_JoystickGetVendor(device); ++ sdldev.product_id = pSDL_JoystickGetProduct(device); ++ }else{ ++ sdldev.vendor_id = 0x01; ++ sdldev.product_id = SDL_JoystickInstanceID(device) + 1; ++ } + + if (!have_sdldevs) + new_sdldevs = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SDLDev)); +From aafd9a6a7fc58b609f1fc5ee17d33aa49cbbe250 Mon Sep 17 00:00:00 2001 +From: Andrew Eikum +Date: Tue, 28 Aug 2018 10:35:19 -0500 +Subject: [PATCH] winebus: Show an ERR on old SDL + +--- + dlls/winebus.sys/bus_sdl.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c +index 55891138c87..8b6f0269965 100644 +--- a/dlls/winebus.sys/bus_sdl.c ++++ b/dlls/winebus.sys/bus_sdl.c +@@ -973,6 +973,9 @@ NTSTATUS WINAPI sdl_driver_init(DRIVER_OBJECT *driver, UNICODE_STRING *registry_ + pSDL_JoystickGetProduct = wine_dlsym(sdl_handle, "SDL_JoystickGetProduct", NULL, 0); + pSDL_JoystickGetProductVersion = wine_dlsym(sdl_handle, "SDL_JoystickGetProductVersion", NULL, 0); + pSDL_JoystickGetVendor = wine_dlsym(sdl_handle, "SDL_JoystickGetVendor", NULL, 0); ++ if(!pSDL_JoystickGetVendor){ ++ ERR("SDL installation is old! Please upgrade to >=2.0.6 to get accurate joystick information.\n"); ++ } + } + + sdl_driver_obj = driver; +From c6ec16ae9643fc34f0d47881d90ff629990dc4a0 Mon Sep 17 00:00:00 2001 +From: Huw Davies +Date: Mon, 22 Oct 2018 14:14:29 +0100 +Subject: [PATCH] dinput: Don't dump a NULL effect. + +Signed-off-by: Huw Davies +--- + dlls/dinput/joystick_sdl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dlls/dinput/joystick_sdl.c b/dlls/dinput/joystick_sdl.c +index f7031524d13..909c63078bb 100644 +--- a/dlls/dinput/joystick_sdl.c ++++ b/dlls/dinput/joystick_sdl.c +@@ -766,7 +766,7 @@ static HRESULT WINAPI JoystickWImpl_CreateEffect(IDirectInputDevice8W *iface, + effect_list_item* new_effect = NULL; + + TRACE("%p %s %p %p %p\n", iface, debugstr_guid(rguid), lpeff, ppdef, pUnkOuter); +- dump_DIEFFECT(lpeff, rguid, 0); ++ if (lpeff) dump_DIEFFECT(lpeff, rguid, 0); + + if(!This->sdldev->has_ff){ + TRACE("No force feedback support\n");