From f93c41ebba32bdef40d5bfcedaa6a635bde7d4ab Mon Sep 17 00:00:00 2001 From: kozec Date: Sun, 30 Jul 2017 22:31:16 +0200 Subject: [PATCH] Another recode, using NeonMan/xfakeinput this time. Fixes #2 --- Makefile | 42 +- README.md | 4 +- .../xinput1_3.h => dumbxinputemu/Xinput.h | 90 +- .../dinput_input.c | 1170 ++++++++--------- dumbxinputemu/dumbxinputemu.h | 51 + xinput1_1/exports.def | 8 + xinput1_1/xinput1_1.c | 54 + xinput1_2/exports.def | 8 + xinput1_2/xinput1_2.c | 54 + xinput1_3/exports.def | 10 + xinput1_3/xinput1_3.c | 69 + xinput1_3/xinput1_3.def | 29 - xinput1_4/exports.def | 10 + xinput1_4/xinput1_4.c | 74 ++ xinput9_1_0/exports.def | 7 + xinput9_1_0/xinput9_1_0.c | 48 + xinput_obiwan.patch | 588 +++++++++ 17 files changed, 1655 insertions(+), 661 deletions(-) rename xinput1_3/xinput1_3.h => dumbxinputemu/Xinput.h (69%) rename xinput1_3/xinput1_3_main.c => dumbxinputemu/dinput_input.c (75%) create mode 100644 dumbxinputemu/dumbxinputemu.h create mode 100644 xinput1_1/exports.def create mode 100644 xinput1_1/xinput1_1.c create mode 100644 xinput1_2/exports.def create mode 100644 xinput1_2/xinput1_2.c create mode 100644 xinput1_3/exports.def create mode 100644 xinput1_3/xinput1_3.c delete mode 100755 xinput1_3/xinput1_3.def create mode 100644 xinput1_4/exports.def create mode 100644 xinput1_4/xinput1_4.c create mode 100644 xinput9_1_0/exports.def create mode 100644 xinput9_1_0/xinput9_1_0.c create mode 100644 xinput_obiwan.patch diff --git a/Makefile b/Makefile index a6a8330..80ec49c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Important stuff CFLAGS = -LIBS = -ldxguid -ldinput -ldinput8 +LIBS = -luuid -ldxguid -ldinput -ldinput8 # (maybe) distro-specific stuff MINGW32 = i686-w64-mingw32 @@ -12,30 +12,52 @@ INCLUDE = include/ # used in as ${USR}/${MINGWxy}/${INCLUDE} # Computed GCC32 = ${BIN}/${MINGW32}-gcc -INCLUDE_DIR32 = -I${USR}/${MINGW32}/include +INCLUDE_DIR32 = -I${USR}/${MINGW32}/include -Idumbxinputemu LIB_DIR32 = -L{USR}/${MINGW32}/lib/ GCC64 = ${BIN}/x86_64-w64-mingw32-gcc -D _WXI_MINGW -INCLUDE_DIR64 = -I${USR}/${MINGW64}/include +INCLUDE_DIR64 = -I${USR}/${MINGW64}/include -Idumbxinputemu LIB_DIR64 = -L{USR}/${MINGW64}/lib/ +.PHONY: default +default: all + # ---- Targets ------- -build/32/xinput1_3.dll: xinput1_3/xinput1_3.def xinput1_3/xinput1_3_main.c +%.32.o: %.c + ${GCC32} ${CFLAGS} ${INCLUDE_DIR32} ${LIB_DIR32} -c $? -o $@ + +%.64.o: %.c + ${GCC64} ${CFLAGS} ${INCLUDE_DIR64} ${LIB_DIR64} -c $? -o $@ + +build/32/xinput1_3.dll: xinput1_3/exports.def dumbxinputemu/dinput_input.32.o xinput1_3/xinput1_3.32.o + mkdir -p build/32/ + ${GCC32} --shared ${CFLAGS} ${INCLUDE_DIR32} ${LIB_DIR32} $? ${LIBS} -o $@ + +build/32/xinput9_1_0.dll: xinput9_1_0/exports.def dumbxinputemu/dinput_input.32.o xinput9_1_0/xinput9_1_0.32.o mkdir -p build/32/ ${GCC32} --shared ${CFLAGS} ${INCLUDE_DIR32} ${LIB_DIR32} $? ${LIBS} -o $@ -build/64/xinput1_3.dll: xinput1_3/xinput1_3.def xinput1_3/xinput1_3_main.c +build/64/xinput1_3.dll: xinput1_3/exports.def dumbxinputemu/dinput_input.64.o xinput1_3/xinput1_3.64.o + mkdir -p build/64/ + ${GCC64} --shared ${CFLAGS} ${INCLUDE_DIR64} ${LIB_DIR64} $? ${LIBS} -o $@ + +build/64/xinput9_1_0.dll: xinput9_1_0/exports.def dumbxinputemu/dinput_input.64.o xinput9_1_0/xinput9_1_0.64.o mkdir -p build/64/ - ${GCC64} -shared ${CFLAGS} ${INCLUDE_DIR64} ${LIB_DIR64} $? ${LIBS} -o $@ + ${GCC64} --shared ${CFLAGS} ${INCLUDE_DIR64} ${LIB_DIR64} $? ${LIBS} -o $@ clean: - rm -f *.o *.dll + rm -f dumbxinputemu/*.o + rm -f xinput1_1/*.o + rm -f xinput1_2/*.o + rm -f xinput1_3/*.o + rm -f xinput1_4/*.o + rm -f xinput9_1_0/*.o rm -f build/32/* rm -f build/64/* -32bit: build/32/xinput1_3.dll +32bit: build/32/xinput1_3.dll build/32/xinput9_1_0.dll -64bit: build/64/xinput1_3.dll +64bit: build/64/xinput1_3.dll build/64/xinput9_1_0.dll -all: build/32/xinput1_3.dll +all: 32bit diff --git a/README.md b/README.md index 5fee4b7..91b8406 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Dumb xinput Emulator xinput dll reimplementation compatibile with DirectInput controllers. Think x360ce without configuration. ##### Usage -Copy xinput1_3.dll next to game executable and start the game. +Copy xinput1_3.dll next to game executable and start the game. For recent games, try copying xinput9_1_0.dll as well if first one doesn't works. ##### Why in the...? This is meant mainly for using Steam Controller or XBox 360 pad with Wine. @@ -15,4 +15,4 @@ This is meant mainly for using Steam Controller or XBox 360 pad with Wine. - run `make` or `make 64bit` for 64bit version ##### Credits -Based on xinput1_3.dll implementation in Wine and [wine-xinput patch by 00cpxxx](https://github.com/00cpxxx/wine-xinput) +Based on xinput1_3.dll implementation in Wine, [wine-xinput patch by 00cpxxx](https://github.com/00cpxxx/wine-xinput) and [xfakeinput by NeonMan](https://github.com/NeonMan/xfakeinput) diff --git a/xinput1_3/xinput1_3.h b/dumbxinputemu/Xinput.h similarity index 69% rename from xinput1_3/xinput1_3.h rename to dumbxinputemu/Xinput.h index 0b9b054..d3ca726 100644 --- a/xinput1_3/xinput1_3.h +++ b/dumbxinputemu/Xinput.h @@ -17,13 +17,11 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef __XINPUT1_3_H -#define __XINPUT1_3_H +#ifndef __WINE_XINPUT_H +#define __WINE_XINPUT_H #include -#define EXPORT __declspec(dllexport) - /* * Bitmasks for the joysticks buttons, determines what has * been pressed on the joystick, these need to be mapped @@ -55,6 +53,56 @@ #define XINPUT_KEYSTROKE_KEYUP 0x0002 #define XINPUT_KEYSTROKE_REPEAT 0x0004 +/* + * Defines the codes which are returned by XInputGetKeystroke + */ + +#define VK_PAD_A 0x5800 +#define VK_PAD_B 0x5801 +#define VK_PAD_X 0x5802 +#define VK_PAD_Y 0x5803 +#define VK_PAD_RSHOULDER 0x5804 +#define VK_PAD_LSHOULDER 0x5805 +#define VK_PAD_LTRIGGER 0x5806 +#define VK_PAD_RTRIGGER 0x5807 +#define VK_PAD_DPAD_UP 0x5810 +#define VK_PAD_DPAD_DOWN 0x5811 +#define VK_PAD_DPAD_LEFT 0x5812 +#define VK_PAD_DPAD_RIGHT 0x5813 +#define VK_PAD_START 0x5814 +#define VK_PAD_BACK 0x5815 +#define VK_PAD_LTHUMB_PRESS 0x5816 +#define VK_PAD_RTHUMB_PRESS 0x5817 +#define VK_PAD_LTHUMB_UP 0x5820 +#define VK_PAD_LTHUMB_DOWN 0x5821 +#define VK_PAD_LTHUMB_RIGHT 0x5822 +#define VK_PAD_LTHUMB_LEFT 0x5823 +#define VK_PAD_LTHUMB_UPLEFT 0x5824 +#define VK_PAD_LTHUMB_UPRIGHT 0x5825 +#define VK_PAD_LTHUMB_DOWNRIGHT 0x5826 +#define VK_PAD_LTHUMB_DOWNLEFT 0x5827 +#define VK_PAD_RTHUMB_UP 0x5830 +#define VK_PAD_RTHUMB_DOWN 0x5831 +#define VK_PAD_RTHUMB_RIGHT 0x5832 +#define VK_PAD_RTHUMB_LEFT 0x5833 +#define VK_PAD_RTHUMB_UPLEFT 0x5834 +#define VK_PAD_RTHUMB_UPRIGHT 0x5835 +#define VK_PAD_RTHUMB_DOWNRIGHT 0x5836 +#define VK_PAD_RTHUMB_DOWNLEFT 0x5837 + +/* + * Deadzones are for analogue joystick controls on the joypad + * which determine when input should be assumed to be in the + * middle of the pad. This is a threshold to stop a joypad + * controlling the game when the player isn't touching the + * controls. + */ + +#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849 +#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689 +#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD 30 + + /* * Defines what type of abilities the type of joystick has * DEVTYPE_GAMEPAD is available for all joysticks, however @@ -122,27 +170,11 @@ typedef struct _XINPUT_GAMEPAD { SHORT sThumbRY; } XINPUT_GAMEPAD, *PXINPUT_GAMEPAD; -typedef struct _XINPUT_GAMEPAD_EX { - WORD wButtons; - BYTE bLeftTrigger; - BYTE bRightTrigger; - SHORT sThumbLX; - SHORT sThumbLY; - SHORT sThumbRX; - SHORT sThumbRY; - DWORD dwPaddingReserved; -} XINPUT_GAMEPAD_EX, *PXINPUT_GAMEPAD_EX; - typedef struct _XINPUT_STATE { DWORD dwPacketNumber; XINPUT_GAMEPAD Gamepad; } XINPUT_STATE, *PXINPUT_STATE; -typedef struct _XINPUT_STATE_EX { - DWORD dwPacketNumber; - XINPUT_GAMEPAD_EX Gamepad; -} XINPUT_STATE_EX, *PXINPUT_STATE_EX; - /* * Defines the structure of how much vibration is set on both the * right and left motors in a joystick. If you're not using a 360 @@ -192,18 +224,16 @@ typedef struct _XINPUT_BATTERY_INFORMATION extern "C" { #endif -void EXPORT XInputEnable(BOOL); -DWORD EXPORT XInputSetState(DWORD, XINPUT_VIBRATION*); -DWORD EXPORT XInputGetState(DWORD, XINPUT_STATE*); -DWORD EXPORT XInputGetKeystroke(DWORD, DWORD, PXINPUT_KEYSTROKE); -DWORD EXPORT XInputGetCapabilities(DWORD, DWORD, XINPUT_CAPABILITIES*); -DWORD EXPORT XInputGetDSoundAudioDeviceGuids(DWORD, GUID*, GUID*); -DWORD EXPORT XInputGetBatteryInformation(DWORD, BYTE, XINPUT_BATTERY_INFORMATION*); - -DWORD EXPORT XInputGetStateEx(DWORD, XINPUT_STATE_EX*); +void WINAPI XInputEnable(WINBOOL); +DWORD WINAPI XInputSetState(DWORD, XINPUT_VIBRATION*); +DWORD WINAPI XInputGetState(DWORD, XINPUT_STATE*); +DWORD WINAPI XInputGetKeystroke(DWORD, DWORD, PXINPUT_KEYSTROKE); +DWORD WINAPI XInputGetCapabilities(DWORD, DWORD, XINPUT_CAPABILITIES*); +DWORD WINAPI XInputGetDSoundAudioDeviceGuids(DWORD, GUID*, GUID*); +DWORD WINAPI XInputGetBatteryInformation(DWORD, BYTE, XINPUT_BATTERY_INFORMATION*); #ifdef __cplusplus } #endif -#endif /* __XINPUT1_3_H */ +#endif /* __WINE_XINPUT_H */ diff --git a/xinput1_3/xinput1_3_main.c b/dumbxinputemu/dinput_input.c similarity index 75% rename from xinput1_3/xinput1_3_main.c rename to dumbxinputemu/dinput_input.c index efe729c..932f621 100644 --- a/xinput1_3/xinput1_3_main.c +++ b/dumbxinputemu/dinput_input.c @@ -1,592 +1,582 @@ -/* - * The Wine project - Xinput Joystick Library - * - * Copyright 2008 Andrew Fenn - * Copyright 2016 Bruno Jesus - * - * 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 "xinput1_3.h" - -#include "windef.h" -#include "winbase.h" -#include "winerror.h" -#include "unknwn.h" -#include - -#define DIRECTINPUT_VERSION 0x0800 -#include "dinput.h" - -#ifndef TRACE - // Unavailable outside wine - #define TRACE(format, ...) printf("TRACE[%d] " format, __LINE__, ## __VA_ARGS__) - #define FIXME(...) do { } while(0) - #define WARN(...) do { } while(0) - #define ERR(...) do { } while(0) -#endif - -#ifndef DECLSPEC_HOTPATCH - #define DECLSPEC_HOTPATCH -#endif - -#ifndef XINPUT_GAMEPAD_GUIDE - #define XINPUT_GAMEPAD_GUIDE 0x0400 -#endif - -struct CapsFlags -{ - BOOL wireless, jedi, pov; - int axes, buttons; -}; - -static struct ControllerMap -{ - LPDIRECTINPUTDEVICE8A device; - BOOL connected, acquired; - struct CapsFlags caps; - XINPUT_STATE_EX state_ex; - XINPUT_VIBRATION vibration; - BOOL vibration_dirty; - - DIEFFECT effect_data; - LPDIRECTINPUTEFFECT effect_instance; -} controllers[XUSER_MAX_COUNT]; - - -static struct -{ - LPDIRECTINPUT8A iface; - BOOL enabled; - int mapped; -} dinput; - -#define STARTUP_DINPUT if (!dinput.iface) dinput_start(); - - -/* ========================= Internal functions ============================= */ - -static BOOL dinput_is_good(const LPDIRECTINPUTDEVICE8A device, struct CapsFlags *caps) -{ - HRESULT hr; - DIPROPDWORD property; - DIDEVCAPS dinput_caps; - static const unsigned long wireless_products[] = { - MAKELONG(0x045e, 0x0291) /* microsoft receiver */, - MAKELONG(0x045e, 0x0719) /* microsoft controller */, - MAKELONG(0x0738, 0x4556) /* mad catz */, - MAKELONG(0x0e6f, 0x0003) /* logitech */, - MAKELONG(0x0e6f, 0x0005) /* eclipse */, - MAKELONG(0x0e6f, 0x0006) /* edge */, - MAKELONG(0x102c, 0xff0c) /* joytech */ - }; - int i; - - dinput_caps.dwSize = sizeof(dinput_caps); - hr = IDirectInputDevice_GetCapabilities(device, &dinput_caps); - if (FAILED(hr)) - return FALSE; - - property.diph.dwSize = sizeof(property); - property.diph.dwHeaderSize = sizeof(property.diph); - property.diph.dwObj = 0; - property.diph.dwHow = DIPH_DEVICE; - - hr = IDirectInputDevice_GetProperty(device, DIPROP_VIDPID, &property.diph); - if (FAILED(hr)) - return FALSE; - - if (dinput_caps.dwAxes < 2 || dinput_caps.dwButtons < 8) - return FALSE; - - caps->axes = dinput_caps.dwAxes; - caps->buttons = dinput_caps.dwButtons; - caps->wireless = FALSE; - caps->jedi = !!(dinput_caps.dwFlags & DIDC_FORCEFEEDBACK); - caps->pov = !!dinput_caps.dwPOVs; - - for (i = 0; i < sizeof(wireless_products) / sizeof(wireless_products[0]); i++) - if (property.dwData == wireless_products[i]) - { - caps->wireless = TRUE; - break; - } - - if (dinput_caps.dwAxes == 6 && dinput_caps.dwButtons == 11 && dinput_caps.dwPOVs == 1) - TRACE("This controller has the same number of buttons/axes from xbox 360, should work...\n"); - else - FIXME("This is not a known xbox controller, using anyway. Expect problems!\n"); - - return TRUE; -} - -static BOOL dinput_set_range(const LPDIRECTINPUTDEVICE8A device) -{ - HRESULT hr; - DIPROPRANGE property; - - property.diph.dwSize = sizeof(property); - property.diph.dwHeaderSize = sizeof(property.diph); - property.diph.dwHow = DIPH_DEVICE; - property.diph.dwObj = 0; - property.lMin = -32767; - property.lMax = +32767; - - hr = IDirectInputDevice_SetProperty(device, DIPROP_RANGE, &property.diph); - if (FAILED(hr)) - { - WARN("Failed to set axis range (0x%x)\n", hr); - return FALSE; - } - return TRUE; -} - -static void dinput_joystate_to_xinput(DIJOYSTATE2 *js, XINPUT_GAMEPAD_EX *gamepad, struct CapsFlags *caps) -{ - static const int xbox_buttons[] = { - XINPUT_GAMEPAD_A, - XINPUT_GAMEPAD_B, - XINPUT_GAMEPAD_X, - XINPUT_GAMEPAD_Y, - XINPUT_GAMEPAD_LEFT_SHOULDER, - XINPUT_GAMEPAD_RIGHT_SHOULDER, - XINPUT_GAMEPAD_BACK, - XINPUT_GAMEPAD_START, - XINPUT_GAMEPAD_GUIDE, - XINPUT_GAMEPAD_LEFT_THUMB, - XINPUT_GAMEPAD_RIGHT_THUMB - }; - int i, buttons; - - gamepad->dwPaddingReserved = 0; - gamepad->wButtons = 0x0000; - /* First the D-Pad which is recognized as a POV in dinput */ - if (caps->pov) - { - switch (js->rgdwPOV[0]) - { - case 0 : gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_UP; break; - case 4500 : gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_UP; /* fall through */ - case 9000 : gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT; break; - case 13500: gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT; /* fall through */ - case 18000: gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_DOWN; break; - case 22500: gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_DOWN; /* fall through */ - case 27000: gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_LEFT; break; - case 31500: gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_DPAD_UP; - } - } - - /* Buttons */ - buttons = min(caps->buttons, sizeof(xbox_buttons) / sizeof(*xbox_buttons)); - for (i = 0; i < buttons; i++) - if (js->rgbButtons[i] & 0x80) - gamepad->wButtons |= xbox_buttons[i]; - - /* Axes */ - gamepad->sThumbLX = js->lX; - gamepad->sThumbLY = -js->lY; - if (caps->axes >= 4) - { - gamepad->sThumbRX = js->lRx; - gamepad->sThumbRY = -js->lRy; - } - else - gamepad->sThumbRX = gamepad->sThumbRY = 0; - - /* Both triggers */ - if (caps->axes >= 6) - { - gamepad->bLeftTrigger = (255 * (js->lZ + 32767)) / 32767; - gamepad->bRightTrigger = (255 * (js->lRz + 32767)) / 32767; - } - else - gamepad->bLeftTrigger = gamepad->bRightTrigger = 0; -} - -static void dinput_fill_effect(DIEFFECT *effect) -{ - static DWORD axes[2] = {DIJOFS_X, DIJOFS_Y}; - static LONG direction[2] = {0, 0}; - - effect->dwSize = sizeof(effect); - effect->dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; - effect->dwDuration = INFINITE; - effect->dwGain = 0; - effect->dwTriggerButton = DIEB_NOTRIGGER; - effect->cAxes = sizeof(axes) / sizeof(axes[0]); - effect->rgdwAxes = axes; - effect->rglDirection = direction; -} - -static void dinput_send_effect(int index, int power) -{ - HRESULT hr; - DIPERIODIC periodic; - DIEFFECT *effect = &controllers[index].effect_data; - LPDIRECTINPUTEFFECT *instance = &controllers[index].effect_instance; - - if (!*instance) - dinput_fill_effect(effect); - - effect->cbTypeSpecificParams = sizeof(periodic); - effect->lpvTypeSpecificParams = &periodic; - - periodic.dwMagnitude = power; - periodic.dwPeriod = DI_SECONDS; /* 1 second */ - periodic.lOffset = 0; - periodic.dwPhase = 0; - - if (!*instance) - { - hr = IDirectInputDevice8_CreateEffect(controllers[index].device, &GUID_Square, - effect, instance, NULL); - if (FAILED(hr)) - { - WARN("Failed to create effect (0x%x)\n", hr); - return; - } - if (!*instance) - { - WARN("Effect not returned???\n"); - return; - } - - hr = IDirectInputEffect_SetParameters(*instance, effect, DIEP_AXES | DIEP_DIRECTION | DIEP_NODOWNLOAD); - if (FAILED(hr)) - { - // TODO: I may just introduce memory leak - // IUnknown_Release(*instance); - *instance = NULL; - WARN("Failed to configure effect (0x%x)\n", hr); - return; - } - } - - hr = IDirectInputEffect_SetParameters(*instance, effect, DIEP_TYPESPECIFICPARAMS | DIEP_START); - if (FAILED(hr)) - { - WARN("Failed to play effect (0x%x)\n", hr); - return; - } -} - -static BOOL CALLBACK dinput_enum_callback(const DIDEVICEINSTANCEA *instance, void *context) -{ - LPDIRECTINPUTDEVICE8A device; - HRESULT hr; - - if (dinput.mapped == sizeof(controllers) / sizeof(*controllers)) - return DIENUM_STOP; - - hr = IDirectInput_CreateDevice(dinput.iface, &instance->guidInstance, &device, NULL); - if (FAILED(hr)) - return DIENUM_CONTINUE; - - if (!dinput_is_good(device, &controllers[dinput.mapped].caps)) - { - IDirectInput_Release(device); - return DIENUM_CONTINUE; - } - - if (!dinput_set_range(device)) - { - IDirectInput_Release(device); - return DIENUM_CONTINUE; - } - - controllers[dinput.mapped].connected = TRUE; - controllers[dinput.mapped].device = device; - dinput.mapped++; - - return DIENUM_CONTINUE; -} - -static void dinput_start(void) -{ - HRESULT hr; - - hr = DirectInput8Create(GetModuleHandleA(NULL), 0x0800, &IID_IDirectInput8A, - (void **)&dinput.iface, NULL); - if (FAILED(hr)) - { - ERR("Failed to create dinput8 interface, no xinput controller support (0x%x)\n", hr); - return; - } - - hr = IDirectInput8_EnumDevices(dinput.iface, DI8DEVCLASS_GAMECTRL, - dinput_enum_callback, NULL, DIEDFL_ATTACHEDONLY); - if (FAILED(hr)) - { - ERR("Failed to enumerate dinput8 devices, no xinput controller support (0x%x)\n", hr); - return; - } - - dinput.enabled = TRUE; -} - -static void dinput_update(int index) -{ - HRESULT hr; - DIJOYSTATE2 data; - XINPUT_GAMEPAD_EX gamepad; - - if (dinput.enabled) - { - if (!controllers[index].acquired) - { - IDirectInputDevice8_SetDataFormat(controllers[index].device, &c_dfDIJoystick2); - hr = IDirectInputDevice8_Acquire(controllers[index].device); - if (FAILED(hr)) - { - WARN("Failed to acquire game controller (0x%x)\n", hr); - return; - } - controllers[index].acquired = TRUE; - } - - IDirectInputDevice8_Poll(controllers[index].device); - hr = IDirectInputDevice_GetDeviceState(controllers[index].device, sizeof(data), &data); - if (FAILED(hr)) - { - if (hr == DIERR_INPUTLOST) - controllers[index].acquired = FALSE; - WARN("Failed to get game controller state (0x%x)\n", hr); - return; - } - dinput_joystate_to_xinput(&data, &gamepad, &controllers[index].caps); - } - else - memset(&gamepad, 0, sizeof(gamepad)); - - if (memcmp(&controllers[index].state_ex.Gamepad, &gamepad, sizeof(gamepad))) - { - controllers[index].state_ex.Gamepad = gamepad; - controllers[index].state_ex.dwPacketNumber++; - } -} - - - -/* ============================ Dll Functions =============================== */ - -BOOL APIENTRY DllMain( HMODULE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved - ) -{ - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - DisableThreadLibraryCalls(hModule); - break; - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} - - - -void EXPORT XInputEnable(BOOL enable) -{ - /* Setting to false will stop messages from XInputSetState being sent - to the controllers. Setting to true will send the last vibration - value (sent to XInputSetState) to the controller and allow messages to - be sent */ - TRACE("(%d)\n", enable); - - STARTUP_DINPUT - - if((dinput.enabled = enable)) - { - int i; - /* Apply the last vibration status that was sent to the controller - * while xinput was disabled. */ - for (i = 0; i < sizeof(controllers) / sizeof(*controllers); i++) - { - if (controllers[i].connected && controllers[i].vibration_dirty) - XInputSetState(i, &controllers[i].vibration); - } - } -} - -DWORD EXPORT XInputSetState(DWORD index, XINPUT_VIBRATION* vibration) -{ - // TRACE("(%u %p)\n", index, vibration); - - STARTUP_DINPUT - - if (index >= XUSER_MAX_COUNT) - return ERROR_BAD_ARGUMENTS; - if (!controllers[index].connected) - return ERROR_DEVICE_NOT_CONNECTED; - - /* Check if we really have to do all the process */ - if (!controllers[index].vibration_dirty && - !memcmp(&controllers[index].vibration, vibration, sizeof(*vibration))) - return ERROR_SUCCESS; - - controllers[index].vibration = *vibration; - controllers[index].vibration_dirty = !dinput.enabled; - - if (dinput.enabled && controllers[index].caps.jedi) - { - int power; - /* FIXME: we can't set the speed of each motor so do an average */ - power = DI_FFNOMINALMAX * (vibration->wLeftMotorSpeed + vibration->wRightMotorSpeed) / 2 / 0xFFFF; - - TRACE("Vibration left/right speed %d/%d translated to %d\n\n", - vibration->wLeftMotorSpeed, vibration->wRightMotorSpeed, power); - dinput_send_effect(index, power); - } - - return ERROR_SUCCESS; -} - - -DWORD EXPORT DECLSPEC_HOTPATCH XInputGetStateEx(DWORD index, XINPUT_STATE_EX* state_ex) -{ - TRACE("(%u %p)\n", index, state_ex); - - STARTUP_DINPUT - - if (index >= XUSER_MAX_COUNT) - return ERROR_BAD_ARGUMENTS; - if (!controllers[index].connected) - return ERROR_DEVICE_NOT_CONNECTED; - - dinput_update(index); - //broforce does not pass a correct XINPUT_STATE_EX, so only copy the old struct size - *(XINPUT_GAMEPAD *)state_ex = *(XINPUT_GAMEPAD *)&controllers[index].state_ex; - - return ERROR_SUCCESS; -} - - -DWORD EXPORT DECLSPEC_HOTPATCH XInputGetState(DWORD index, XINPUT_STATE* state) -{ - union - { - XINPUT_STATE state; - XINPUT_STATE_EX state_ex; - } xinput; - DWORD ret; - - ret = XInputGetStateEx(index, &xinput.state_ex); - if (ret != ERROR_SUCCESS) - return ret; - - /* The main difference between this and the Ex version is the media guide button */ - *state = xinput.state; - state->Gamepad.wButtons &= ~XINPUT_GAMEPAD_GUIDE; - - return ERROR_SUCCESS; -} - - -DWORD EXPORT XInputGetKeystroke(DWORD index, DWORD reserved, PXINPUT_KEYSTROKE keystroke) -{ - FIXME("(index %u, reserved %u, keystroke %p) Stub!\n", index, reserved, keystroke); - - if (index >= XUSER_MAX_COUNT) - return ERROR_BAD_ARGUMENTS; - if (!controllers[index].connected) - return ERROR_DEVICE_NOT_CONNECTED; - - return ERROR_NOT_SUPPORTED; -} - -/* Not defined anywhere ??? */ -#define XINPUT_CAPS_FFB_SUPPORTED 0x0001 -#define XINPUT_CAPS_WIRELESS 0x0002 -#define XINPUT_CAPS_NO_NAVIGATION 0x0010 - -DWORD EXPORT XInputGetCapabilities(DWORD index, DWORD flags, XINPUT_CAPABILITIES* capabilities) -{ - TRACE("(%u %d %p)\n", index, flags, capabilities); - - STARTUP_DINPUT - - if (index >= XUSER_MAX_COUNT) - return ERROR_BAD_ARGUMENTS; - if (!controllers[index].connected) - return ERROR_DEVICE_NOT_CONNECTED; - - capabilities->Type = XINPUT_DEVTYPE_GAMEPAD; - capabilities->SubType = XINPUT_DEVSUBTYPE_GAMEPAD; - - capabilities->Flags = 0; - if (controllers[index].caps.jedi) - capabilities->Flags |= XINPUT_CAPS_FFB_SUPPORTED; - //if (controllers[index].caps.wireless) - // capabilities->Flags |= XINPUT_CAPS_WIRELESS; - if (!controllers[index].caps.pov) - capabilities->Flags |= XINPUT_CAPS_NO_NAVIGATION; - - dinput_update(index); - - capabilities->Vibration = controllers[index].vibration; - capabilities->Gamepad = *(XINPUT_GAMEPAD *)&controllers[index].state_ex.Gamepad; - - return ERROR_SUCCESS; -} - -DWORD EXPORT XInputGetDSoundAudioDeviceGuids(DWORD index, GUID* render_guid, GUID* capture_guid) -{ - FIXME("(index %u, render guid %p, capture guid %p) Stub!\n", index, render_guid, capture_guid); - - if (index >= XUSER_MAX_COUNT) - return ERROR_BAD_ARGUMENTS; - if (!controllers[index].connected) - return ERROR_DEVICE_NOT_CONNECTED; - - return ERROR_NOT_SUPPORTED; -} - -DWORD EXPORT XInputGetBatteryInformation(DWORD index, BYTE type, XINPUT_BATTERY_INFORMATION* battery) -{ - TRACE("(%u %u %p) Stub!\n", index, type, battery); - - STARTUP_DINPUT - - if (index >= XUSER_MAX_COUNT) - return ERROR_BAD_ARGUMENTS; - if (!controllers[index].connected) - return ERROR_DEVICE_NOT_CONNECTED; - - battery->BatteryType = BATTERY_TYPE_WIRED; - battery->BatteryLevel = BATTERY_LEVEL_FULL; - - - return ERROR_SUCCESS; -} - - -/* ============================ Additional, undocumented functions =============================== */ -DWORD EXPORT XInputWaitForGuideButton(DWORD dwUserIndex, DWORD dwFlag, LPVOID pVoid) -{ - return ERROR_SUCCESS; -} - -DWORD EXPORT XInputCancelGuideButtonWait(DWORD dwUserIndex) -{ - return ERROR_SUCCESS; -} - -DWORD EXPORT XInputPowerOffController(DWORD dwUserIndex) +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "dumbxinputemu.h" +#include +#include + +#define DIRECTINPUT_VERSION 0x0800 +#include "dinput.h" + +#ifndef TRACE + // Available only in Wine + // #define TRACE(format, ...) printf("TRACE[%d] " format, __LINE__, ## __VA_ARGS__) + #define TRACE(...) do { } while(0) + #define DPRINT(...) do { } while(0) + #define FIXME(...) do { } while(0) + #define WARN(...) do { } while(0) + #define ERR(format, ...) printf("ERR[%d] " format, __LINE__, ## __VA_ARGS__) +#endif + +struct CapsFlags { + BOOL wireless, jedi, pov; + int axes, buttons; +}; + +static struct ControllerMap { + LPDIRECTINPUTDEVICE8A device; + BOOL connected, acquired; + struct CapsFlags caps; + XINPUT_STATE_EX state_ex; + XINPUT_VIBRATION vibration; + BOOL vibration_dirty; + + DIEFFECT effect_data; + LPDIRECTINPUTEFFECT effect_instance; +} controllers[XUSER_MAX_COUNT]; + +static struct { + LPDIRECTINPUT8A iface; + BOOL enabled; + int mapped; +} dinput; + +static XINPUT_CAPABILITIES default_capabilities = { + (BYTE) 0x1, + (BYTE) 0x1, + (WORD) 0x4, + { + /*wButtons*/ (WORD) 0xF3FF, + /*bLeftTrigger*/ (BYTE) 0x1, + /*bRightTrigger*/ (BYTE) 0x1, + /*sThumbLX*/ (SHORT) 0x1, + /*sThumbLY*/ (SHORT) 0x1, + /*sThumbRX*/ (SHORT) 0x1, + /*sThumbRY*/ (SHORT) 0x1 + }, + { + /*wLeftMotorSpeed*/ (WORD) 0x1, + /*wRightMotorSpeed*/ (WORD) 0x1 + } +}; + +///Initial pad state, from a real pad at rest +static XINPUT_STATE initial_state = { + (DWORD)0x1000, /*dwPacketNumber*/ + { + (WORD) 0, + (BYTE) 0, + (BYTE) 0, + (SHORT) 3580, + (SHORT) -4118, + (SHORT) -3524, + (SHORT) 329 + } +}; + +/* ========================= Internal functions ============================= */ + +bool initialized = FALSE; +XINPUT_STATE pad_states[4]; + + +static BOOL dinput_is_good(const LPDIRECTINPUTDEVICE8A device, struct CapsFlags *caps) +{ + HRESULT hr; + DIPROPDWORD property; + DIDEVCAPS dinput_caps; + static const unsigned long wireless_products[] = { + MAKELONG(0x045e, 0x0291) /* microsoft receiver */, + MAKELONG(0x045e, 0x0719) /* microsoft controller */, + MAKELONG(0x0738, 0x4556) /* mad catz */, + MAKELONG(0x0e6f, 0x0003) /* logitech */, + MAKELONG(0x0e6f, 0x0005) /* eclipse */, + MAKELONG(0x0e6f, 0x0006) /* edge */, + MAKELONG(0x102c, 0xff0c) /* joytech */ + }; + int i; + + dinput_caps.dwSize = sizeof(dinput_caps); + hr = IDirectInputDevice_GetCapabilities(device, &dinput_caps); + if (FAILED(hr)) + return FALSE; + + property.diph.dwSize = sizeof(property); + property.diph.dwHeaderSize = sizeof(property.diph); + property.diph.dwObj = 0; + property.diph.dwHow = DIPH_DEVICE; + + hr = IDirectInputDevice_GetProperty(device, DIPROP_VIDPID, &property.diph); + if (FAILED(hr)) + return FALSE; + + if (dinput_caps.dwAxes < 2 || dinput_caps.dwButtons < 8) + return FALSE; + + caps->axes = dinput_caps.dwAxes; + caps->buttons = dinput_caps.dwButtons; + caps->wireless = FALSE; + caps->jedi = !!(dinput_caps.dwFlags & DIDC_FORCEFEEDBACK); + caps->pov = !!dinput_caps.dwPOVs; + + for (i = 0; i < sizeof(wireless_products) / sizeof(wireless_products[0]); i++) + if (property.dwData == wireless_products[i]) + { + caps->wireless = TRUE; + break; + } + + if (dinput_caps.dwAxes == 6 && dinput_caps.dwButtons == 11 && dinput_caps.dwPOVs == 1) + TRACE("This controller has the same number of buttons/axes from xbox 360, should work...\n"); + else + FIXME("This is not a known xbox controller, using anyway. Expect problems!\n"); + + return TRUE; +} + + +static BOOL dinput_set_range(const LPDIRECTINPUTDEVICE8A device) +{ + HRESULT hr; + DIPROPRANGE property; + + property.diph.dwSize = sizeof(property); + property.diph.dwHeaderSize = sizeof(property.diph); + property.diph.dwHow = DIPH_DEVICE; + property.diph.dwObj = 0; + property.lMin = -32767; + property.lMax = +32767; + + hr = IDirectInputDevice_SetProperty(device, DIPROP_RANGE, &property.diph); + if (FAILED(hr)) + { + WARN("Failed to set axis range (0x%x)\n", hr); + return FALSE; + } + return TRUE; +} + + +static void dinput_joystate_to_xinput(DIJOYSTATE2 *js, XINPUT_GAMEPAD_EX *gamepad, struct CapsFlags *caps) +{ + static const int xbox_buttons[] = { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_BACK, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_GUIDE, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_THUMB + }; + int i, buttons; + + gamepad->dwPaddingReserved = 0; + gamepad->wButtons = 0x0000; + /* First the D-Pad which is recognized as a POV in dinput */ + if (caps->pov) + { + switch (js->rgdwPOV[0]) + { + case 0 : gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_UP; break; + case 4500 : gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_UP; /* fall through */ + case 9000 : gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT; break; + case 13500: gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT; /* fall through */ + case 18000: gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_DOWN; break; + case 22500: gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_DOWN; /* fall through */ + case 27000: gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_LEFT; break; + case 31500: gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_DPAD_UP; + } + } + + /* Buttons */ + buttons = min(caps->buttons, sizeof(xbox_buttons) / sizeof(*xbox_buttons)); + for (i = 0; i < buttons; i++) + if (js->rgbButtons[i] & 0x80) + gamepad->wButtons |= xbox_buttons[i]; + + /* Axes */ + gamepad->sThumbLX = js->lX; + gamepad->sThumbLY = -js->lY; + if (caps->axes >= 4) + { + gamepad->sThumbRX = js->lRx; + gamepad->sThumbRY = -js->lRy; + } + else + gamepad->sThumbRX = gamepad->sThumbRY = 0; + + /* Both triggers */ + if (caps->axes >= 6) + { + gamepad->bLeftTrigger = (255 * (js->lZ + 32767)) / 32767; + gamepad->bRightTrigger = (255 * (js->lRz + 32767)) / 32767; + } + else + gamepad->bLeftTrigger = gamepad->bRightTrigger = 0; +} + + +static void dinput_fill_effect(DIEFFECT *effect) +{ + static DWORD axes[2] = {DIJOFS_X, DIJOFS_Y}; + static LONG direction[2] = {0, 0}; + + effect->dwSize = sizeof(effect); + effect->dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; + effect->dwDuration = INFINITE; + effect->dwGain = 0; + effect->dwTriggerButton = DIEB_NOTRIGGER; + effect->cAxes = sizeof(axes) / sizeof(axes[0]); + effect->rgdwAxes = axes; + effect->rglDirection = direction; +} + + +static void dinput_send_effect(int index, int power) +{ + HRESULT hr; + DIPERIODIC periodic; + DIEFFECT *effect = &controllers[index].effect_data; + LPDIRECTINPUTEFFECT *instance = &controllers[index].effect_instance; + + if (!*instance) + dinput_fill_effect(effect); + + effect->cbTypeSpecificParams = sizeof(periodic); + effect->lpvTypeSpecificParams = &periodic; + + periodic.dwMagnitude = power; + periodic.dwPeriod = DI_SECONDS; /* 1 second */ + periodic.lOffset = 0; + periodic.dwPhase = 0; + + if (!*instance) + { + hr = IDirectInputDevice8_CreateEffect(controllers[index].device, &GUID_Square, + effect, instance, NULL); + if (FAILED(hr)) + { + WARN("Failed to create effect (0x%x)\n", hr); + return; + } + if (!*instance) + { + WARN("Effect not returned???\n"); + return; + } + + hr = IDirectInputEffect_SetParameters(*instance, effect, DIEP_AXES | DIEP_DIRECTION | DIEP_NODOWNLOAD); + if (FAILED(hr)) + { + // TODO: I may just introduce memory leak + // IUnknown_Release(*instance); + *instance = NULL; + WARN("Failed to configure effect (0x%x)\n", hr); + return; + } + } + + hr = IDirectInputEffect_SetParameters(*instance, effect, DIEP_TYPESPECIFICPARAMS | DIEP_START); + if (FAILED(hr)) + { + WARN("Failed to play effect (0x%x)\n", hr); + return; + } +} + + +static BOOL CALLBACK dinput_enum_callback(const DIDEVICEINSTANCEA *instance, void *context) +{ + LPDIRECTINPUTDEVICE8A device; + HRESULT hr; + + if (dinput.mapped == sizeof(controllers) / sizeof(*controllers)) + return DIENUM_STOP; + + hr = IDirectInput_CreateDevice(dinput.iface, &instance->guidInstance, &device, NULL); + if (FAILED(hr)) + return DIENUM_CONTINUE; + + if (!dinput_is_good(device, &controllers[dinput.mapped].caps)) + { + IDirectInput_Release(device); + return DIENUM_CONTINUE; + } + + if (!dinput_set_range(device)) + { + IDirectInput_Release(device); + return DIENUM_CONTINUE; + } + + controllers[dinput.mapped].connected = TRUE; + controllers[dinput.mapped].device = device; + dinput.mapped++; + + return DIENUM_CONTINUE; +} + + +static void dinput_start(void) +{ + HRESULT hr; + + hr = DirectInput8Create(GetModuleHandleA(NULL), 0x0800, &IID_IDirectInput8A, + (void **)&dinput.iface, NULL); + if (FAILED(hr)) + { + ERR("Failed to create dinput8 interface, no xinput controller support (0x%x)\n", hr); + return; + } + + hr = IDirectInput8_EnumDevices(dinput.iface, DI8DEVCLASS_GAMECTRL, + dinput_enum_callback, NULL, DIEDFL_ATTACHEDONLY); + if (FAILED(hr)) + { + ERR("Failed to enumerate dinput8 devices, no xinput controller support (0x%x)\n", hr); + return; + } + + dinput.enabled = TRUE; +} + + +static void dinput_update(int index) +{ + HRESULT hr; + DIJOYSTATE2 data; + XINPUT_GAMEPAD_EX gamepad; + + DPRINT("dinput_update: %d\n", index); + + if (dinput.enabled) + { + if (!controllers[index].acquired) + { + IDirectInputDevice8_SetDataFormat(controllers[index].device, &c_dfDIJoystick2); + hr = IDirectInputDevice8_Acquire(controllers[index].device); + if (FAILED(hr)) + { + WARN("Failed to acquire game controller (0x%x)\n", hr); + return; + } + controllers[index].acquired = TRUE; + } + + IDirectInputDevice8_Poll(controllers[index].device); + hr = IDirectInputDevice_GetDeviceState(controllers[index].device, sizeof(data), &data); + if (FAILED(hr)) + { + if (hr == DIERR_INPUTLOST) + controllers[index].acquired = FALSE; + WARN("Failed to get game controller state (0x%x)\n", hr); + return; + } + dinput_joystate_to_xinput(&data, &gamepad, &controllers[index].caps); + } + else + memset(&gamepad, 0, sizeof(gamepad)); + + if (memcmp(&controllers[index].state_ex.Gamepad, &gamepad, sizeof(gamepad))) + { + controllers[index].state_ex.Gamepad = gamepad; + controllers[index].state_ex.dwPacketNumber++; + } +} + + +void dumb_Init(DWORD version) +{ + if (initialized) + return; + dinput_start(); + pad_states[0] = initial_state; + pad_states[1] = initial_state; + pad_states[2] = initial_state; + pad_states[3] = initial_state; + initialized = TRUE; +} + +void dumb_Cleanup() { - return ERROR_SUCCESS; + // Does nothing } + +/* ============================ Dll Functions =============================== */ + +DWORD dumb_XInputGetState(DWORD index, XINPUT_STATE *state, DWORD caller_version) +{ + union + { + XINPUT_STATE state; + XINPUT_STATE_EX state_ex; + } xinput; + DWORD ret; + + DPRINT("XInputGetState: %d\n", index); + + ret = dumb_XInputGetStateEx(index, &xinput.state_ex, caller_version); + if (ret != ERROR_SUCCESS) + return ret; + + /* The main difference between this and the Ex version is the media guide button */ + *state = xinput.state; + state->Gamepad.wButtons &= ~XINPUT_GAMEPAD_GUIDE; + + return ERROR_SUCCESS; +} + + +DWORD dumb_XInputGetStateEx(DWORD index, XINPUT_STATE_EX *state_ex, DWORD caller_version) +{ + TRACE("(%u %p)\n", index, state_ex); + DPRINT("XInputGetStateEx: %d\n", index); + + if (index >= XUSER_MAX_COUNT) + return ERROR_BAD_ARGUMENTS; + if (!controllers[index].connected) + return ERROR_DEVICE_NOT_CONNECTED; + + dinput_update(index); + // broforce does not pass a correct XINPUT_STATE_EX, so only copy the old struct size + *(XINPUT_GAMEPAD *)state_ex = *(XINPUT_GAMEPAD *)&controllers[index].state_ex; + + return ERROR_SUCCESS; +} + + +DWORD dumb_XInputSetState(DWORD index, XINPUT_VIBRATION *vibration, DWORD caller_version) +{ + // TRACE("(%u %p)\n", index, vibration); + + DPRINT("XInputSetState: %d\n", index); + + if (index >= XUSER_MAX_COUNT) + return ERROR_BAD_ARGUMENTS; + if (!controllers[index].connected) + return ERROR_DEVICE_NOT_CONNECTED; + + /* Check if we really have to do all the process */ + if (!controllers[index].vibration_dirty && + !memcmp(&controllers[index].vibration, vibration, sizeof(*vibration))) + return ERROR_SUCCESS; + + controllers[index].vibration = *vibration; + controllers[index].vibration_dirty = !dinput.enabled; + + if (dinput.enabled && controllers[index].caps.jedi) + { + int power; + /* FIXME: we can't set the speed of each motor so do an average */ + power = DI_FFNOMINALMAX * (vibration->wLeftMotorSpeed + vibration->wRightMotorSpeed) / 2 / 0xFFFF; + + TRACE("Vibration left/right speed %d/%d translated to %d\n\n", + vibration->wLeftMotorSpeed, vibration->wRightMotorSpeed, power); + dinput_send_effect(index, power); + } + + return ERROR_SUCCESS; +} + + +void dumb_XInputEnable(BOOL enable) +{ + /* Setting to false will stop messages from XInputSetState being sent + to the controllers. Setting to true will send the last vibration + value (sent to XInputSetState) to the controller and allow messages to + be sent */ + TRACE("(%d)\n", enable); + + DPRINT("XInputEnable: %d\n", enable); + + if ((dinput.enabled = enable)) + { + int i; + /* Apply the last vibration status that was sent to the controller + * while xinput was disabled. */ + for (i = 0; i < sizeof(controllers) / sizeof(*controllers); i++) + { + if (controllers[i].connected && controllers[i].vibration_dirty) + XInputSetState(i, &controllers[i].vibration); + } + } +} + +/* Not defined anywhere ??? */ +#define XINPUT_CAPS_FFB_SUPPORTED 0x0001 +#define XINPUT_CAPS_WIRELESS 0x0002 +#define XINPUT_CAPS_NO_NAVIGATION 0x0010 + +DWORD dumb_XInputGetCapabilities(DWORD index, DWORD flags, + XINPUT_CAPABILITIES *capabilities, DWORD caller_version) +{ + TRACE("(%u %d %p)\n", index, flags, capabilities); + + DPRINT("XInputGetCapabilities: %d\n", index); + + if (index >= XUSER_MAX_COUNT) + return ERROR_BAD_ARGUMENTS; + if (!controllers[index].connected) + return ERROR_DEVICE_NOT_CONNECTED; + + capabilities->Type = XINPUT_DEVTYPE_GAMEPAD; + capabilities->SubType = XINPUT_DEVSUBTYPE_GAMEPAD; + + capabilities->Flags = 0; + if (controllers[index].caps.jedi) + capabilities->Flags |= XINPUT_CAPS_FFB_SUPPORTED; + //if (controllers[index].caps.wireless) + // capabilities->Flags |= XINPUT_CAPS_WIRELESS; + if (!controllers[index].caps.pov) + capabilities->Flags |= XINPUT_CAPS_NO_NAVIGATION; + + dinput_update(index); + + capabilities->Vibration = controllers[index].vibration; + capabilities->Gamepad = *(XINPUT_GAMEPAD *)&controllers[index].state_ex.Gamepad; + + return ERROR_SUCCESS; +} + + +DWORD dumb_XInputGetDSoundAudioDeviceGuids(DWORD dwUserIndex, GUID* pDSoundRenderGuid, + GUID* pDSoundCaptureGuid, DWORD caller_version) +{ + if (dwUserIndex > 3) + return ERROR_DEVICE_NOT_CONNECTED; + *pDSoundRenderGuid = GUID_NULL; + *pDSoundCaptureGuid = GUID_NULL; + return ERROR_SUCCESS; +} + + +DWORD dumb_XInputGetKeystroke(DWORD dwUserIndex, DWORD dwReserved, + PXINPUT_KEYSTROKE pKeystroke, DWORD caller_version) +{ + FIXME("(index %u, reserved %u, keystroke %p) Stub!\n", dwUserIndex, + dwReserved, pKeystroke); + if (dwUserIndex > 3) + return ERROR_DEVICE_NOT_CONNECTED; + return ERROR_EMPTY; +} + + +DWORD dumb_XInputGetBatteryInformation(DWORD dwUserIndex, BYTE devType, + XINPUT_BATTERY_INFORMATION *pBatteryInformation, DWORD caller_version) +{ + pBatteryInformation->BatteryLevel = BATTERY_LEVEL_FULL; + pBatteryInformation->BatteryType = BATTERY_TYPE_WIRED; + return ERROR_SUCCESS; +} + + +DWORD dumb_XInputGetAudioDeviceIds(DWORD dwUserIndex, LPWSTR pRenderDeviceId, + UINT *pRenderCount, LPWSTR pCaptureDeviceId, + UINT *pCaptureCount, DWORD caller_version) +{ + return ERROR_DEVICE_NOT_CONNECTED; +} diff --git a/dumbxinputemu/dumbxinputemu.h b/dumbxinputemu/dumbxinputemu.h new file mode 100644 index 0000000..a1a5f3b --- /dev/null +++ b/dumbxinputemu/dumbxinputemu.h @@ -0,0 +1,51 @@ +#ifndef _dumbxinputemu_h +#define _dumbxinputemu_h + +//Version ID definitions +#define DUMBINPUT_V1_1 11 +#define DUMBINPUT_V1_2 12 +#define DUMBINPUT_V1_3 13 +#define DUMBINPUT_V1_4 14 +#define DUMBINPUT_V9_1_0 910 + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include +#include +#include + +#ifndef XINPUT_GAMEPAD_GUIDE + #define XINPUT_GAMEPAD_GUIDE 0x0400 +#endif + +typedef struct _XINPUT_GAMEPAD_EX { + WORD wButtons; + BYTE bLeftTrigger; + BYTE bRightTrigger; + SHORT sThumbLX; + SHORT sThumbLY; + SHORT sThumbRX; + SHORT sThumbRY; + DWORD dwPaddingReserved; +} XINPUT_GAMEPAD_EX, *PXINPUT_GAMEPAD_EX; + +typedef struct _XINPUT_STATE_EX { + DWORD dwPacketNumber; + XINPUT_GAMEPAD_EX Gamepad; +} XINPUT_STATE_EX, *PXINPUT_STATE_EX; + +void dumb_Init(DWORD version); +void dumb_Cleanup(); + +void dumb_XInputEnable(BOOL enable); +DWORD dumb_XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities, DWORD caller_version); +DWORD dumb_XInputGetDSoundAudioDeviceGuids(DWORD dwUserIndex, GUID* pDSoundRenderGuid, GUID* pDSoundCaptureGuid, DWORD caller_version); +DWORD dumb_XInputGetState(DWORD dwUserIndex, XINPUT_STATE *pState, DWORD caller_version); +DWORD dumb_XInputGetStateEx(DWORD dwUserIndex, XINPUT_STATE_EX *pState_ex, DWORD caller_version); +DWORD dumb_XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration, DWORD caller_version); +DWORD dumb_XInputGetKeystroke(DWORD dwUserIndex, DWORD dwReserved, PXINPUT_KEYSTROKE pKeystroke, DWORD caller_version); +DWORD dumb_XInputGetBatteryInformation(DWORD dwUserIndex, BYTE devType, XINPUT_BATTERY_INFORMATION *pBatteryInformation, DWORD caller_version); +DWORD dumb_XInputGetAudioDeviceIds(DWORD dwUserIndex, LPWSTR pRenderDeviceId, UINT *pRenderCount, LPWSTR pCaptureDeviceId, UINT *pCaptureCount, DWORD caller_version); + +#endif // _dumbxinputemu_h \ No newline at end of file diff --git a/xinput1_1/exports.def b/xinput1_1/exports.def new file mode 100644 index 0000000..6613c12 --- /dev/null +++ b/xinput1_1/exports.def @@ -0,0 +1,8 @@ +LIBRARY Xinput1_1 +EXPORTS + DllMain @1 + XInputEnable @2 + XInputGetCapabilities @3 + XInputGetDSoundAudioDeviceGuids @4 + XInputGetState @5 + XInputSetState @6 diff --git a/xinput1_1/xinput1_1.c b/xinput1_1/xinput1_1.c new file mode 100644 index 0000000..8f218e7 --- /dev/null +++ b/xinput1_1/xinput1_1.c @@ -0,0 +1,54 @@ +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include +#include "dumbxinputemu.h" + +BOOL APIENTRY DllMain(HANDLE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) +{ + if (ul_reason_for_call == DLL_PROCESS_ATTACH) + dumb_Init(DUMBINPUT_V1_1); + else if (ul_reason_for_call == DLL_PROCESS_DETACH) + dumb_Cleanup(); + return TRUE; +} + +void WINAPI XInputEnable( + _In_ BOOL enable + ){ + dumb_XInputEnable(enable); +} + +DWORD WINAPI XInputGetCapabilities( + _In_ DWORD dwUserIndex, + _In_ DWORD dwFlags, + _Out_ XINPUT_CAPABILITIES *pCapabilities + + ){ + return dumb_XInputGetCapabilities(dwUserIndex, dwFlags, pCapabilities, DUMBINPUT_V1_1); +} + +DWORD WINAPI XInputGetDSoundAudioDeviceGuids( + DWORD dwUserIndex, + GUID* pDSoundRenderGuid, + GUID* pDSoundCaptureGuid + ){ + return dumb_XInputGetDSoundAudioDeviceGuids(dwUserIndex, pDSoundRenderGuid, pDSoundCaptureGuid, DUMBINPUT_V1_1); +} + +DWORD WINAPI XInputGetState( + _In_ DWORD dwUserIndex, + _Out_ XINPUT_STATE *pState + + ){ + return dumb_XInputGetState(dwUserIndex, pState, DUMBINPUT_V1_1); +} + +DWORD WINAPI XInputSetState( + _In_ DWORD dwUserIndex, + _Inout_ XINPUT_VIBRATION *pVibration + ){ + return dumb_XInputSetState(dwUserIndex, pVibration, DUMBINPUT_V1_1); +} \ No newline at end of file diff --git a/xinput1_2/exports.def b/xinput1_2/exports.def new file mode 100644 index 0000000..f068c21 --- /dev/null +++ b/xinput1_2/exports.def @@ -0,0 +1,8 @@ +LIBRARY Xinput1_2 +EXPORTS + DllMain @1 + XInputEnable @2 + XInputGetCapabilities @3 + XInputGetDSoundAudioDeviceGuids @4 + XInputGetState @5 + XInputSetState @6 diff --git a/xinput1_2/xinput1_2.c b/xinput1_2/xinput1_2.c new file mode 100644 index 0000000..5815e4a --- /dev/null +++ b/xinput1_2/xinput1_2.c @@ -0,0 +1,54 @@ +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include +#include "dumbxinputemu.h" + +BOOL APIENTRY DllMain(HANDLE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) +{ + if (ul_reason_for_call == DLL_PROCESS_ATTACH) + dumb_Init(DUMBINPUT_V1_2); + else if (ul_reason_for_call == DLL_PROCESS_DETACH) + dumb_Cleanup(); + return TRUE; +} + +void WINAPI XInputEnable( + _In_ BOOL enable + ){ + dumb_XInputEnable(enable); +} + +DWORD WINAPI XInputGetCapabilities( + _In_ DWORD dwUserIndex, + _In_ DWORD dwFlags, + _Out_ XINPUT_CAPABILITIES *pCapabilities + + ){ + return dumb_XInputGetCapabilities(dwUserIndex, dwFlags, pCapabilities, DUMBINPUT_V1_2); +} + +DWORD WINAPI XInputGetDSoundAudioDeviceGuids( + DWORD dwUserIndex, + GUID* pDSoundRenderGuid, + GUID* pDSoundCaptureGuid + ){ + return dumb_XInputGetDSoundAudioDeviceGuids(dwUserIndex, pDSoundRenderGuid, pDSoundCaptureGuid, DUMBINPUT_V1_2); +} + +DWORD WINAPI XInputGetState( + _In_ DWORD dwUserIndex, + _Out_ XINPUT_STATE *pState + + ){ + return dumb_XInputGetState(dwUserIndex, pState, DUMBINPUT_V1_2); +} + +DWORD WINAPI XInputSetState( + _In_ DWORD dwUserIndex, + _Inout_ XINPUT_VIBRATION *pVibration + ){ + return dumb_XInputSetState(dwUserIndex, pVibration, DUMBINPUT_V1_2); +} \ No newline at end of file diff --git a/xinput1_3/exports.def b/xinput1_3/exports.def new file mode 100644 index 0000000..617bf33 --- /dev/null +++ b/xinput1_3/exports.def @@ -0,0 +1,10 @@ +LIBRARY Xinput1_3 +EXPORTS + DllMain @1 + XInputEnable @5 + XInputGetBatteryInformation @7 + XInputGetCapabilities @4 + XInputGetDSoundAudioDeviceGuids @6 + XInputGetKeystroke @8 + XInputGetState @2 + XInputSetState @3 \ No newline at end of file diff --git a/xinput1_3/xinput1_3.c b/xinput1_3/xinput1_3.c new file mode 100644 index 0000000..b37e719 --- /dev/null +++ b/xinput1_3/xinput1_3.c @@ -0,0 +1,69 @@ +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include +#include "dumbxinputemu.h" + +BOOL APIENTRY DllMain(HANDLE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) +{ + if (ul_reason_for_call == DLL_PROCESS_ATTACH) + dumb_Init(DUMBINPUT_V1_3); + else if (ul_reason_for_call == DLL_PROCESS_DETACH) + dumb_Cleanup(); + return TRUE; +} + +void WINAPI XInputEnable( + _In_ BOOL enable + ){ + dumb_XInputEnable(enable); +} + +DWORD WINAPI XInputGetCapabilities( + _In_ DWORD dwUserIndex, + _In_ DWORD dwFlags, + _Out_ XINPUT_CAPABILITIES *pCapabilities + + ){ + return dumb_XInputGetCapabilities(dwUserIndex, dwFlags, pCapabilities, DUMBINPUT_V1_3); +} + +DWORD WINAPI XInputGetDSoundAudioDeviceGuids( + DWORD dwUserIndex, + GUID* pDSoundRenderGuid, + GUID* pDSoundCaptureGuid + ){ + return dumb_XInputGetDSoundAudioDeviceGuids(dwUserIndex, pDSoundRenderGuid, pDSoundCaptureGuid, DUMBINPUT_V1_3); +} + +DWORD WINAPI XInputGetState( + _In_ DWORD dwUserIndex, + _Out_ XINPUT_STATE *pState + + ){ + return dumb_XInputGetState(dwUserIndex, pState, DUMBINPUT_V1_3); +} + +DWORD WINAPI XInputSetState( + _In_ DWORD dwUserIndex, + _Inout_ XINPUT_VIBRATION *pVibration + ){ + return dumb_XInputSetState(dwUserIndex, pVibration, DUMBINPUT_V1_3); +} + +DWORD WINAPI XInputGetKeystroke( + DWORD dwUserIndex, + DWORD dwReserved, + PXINPUT_KEYSTROKE pKeystroke){ + return dumb_XInputGetKeystroke(dwUserIndex, dwReserved, pKeystroke, DUMBINPUT_V1_3); +} + +DWORD WINAPI XInputGetBatteryInformation( + _In_ DWORD dwUserIndex, + _In_ BYTE devType, + _Out_ XINPUT_BATTERY_INFORMATION *pBatteryInformation + ){ + return dumb_XInputGetBatteryInformation(dwUserIndex, devType, pBatteryInformation, DUMBINPUT_V1_3); +} diff --git a/xinput1_3/xinput1_3.def b/xinput1_3/xinput1_3.def deleted file mode 100755 index fe1d6d0..0000000 --- a/xinput1_3/xinput1_3.def +++ /dev/null @@ -1,29 +0,0 @@ -; Taken from x360ce - -LIBRARY "xinput1_3.dll" - -EXPORTS - ; DllMain @1 - XInputGetState @2 - XInputSetState @3 - XInputGetCapabilities @4 - XInputEnable @5 - XInputGetDSoundAudioDeviceGuids @6 - XInputGetBatteryInformation @7 - XInputGetKeystroke @8 - - ; XInput 1.4 - ; XInputGetAudioDeviceIds @10 - - ; XInput 1.3 undocumented - XInputGetStateEx @100 NONAME - XInputWaitForGuideButton @101 NONAME - XInputCancelGuideButtonWait @102 NONAME - XInputPowerOffController @103 NONAME - - ; XInput 1.4 undocumented - ; XInputGetBaseBusInformation @104 NONAME - ; XInputGetCapabilitiesEx @108 NONAME - - ; reset helper - ; Reset @256 \ No newline at end of file diff --git a/xinput1_4/exports.def b/xinput1_4/exports.def new file mode 100644 index 0000000..509a891 --- /dev/null +++ b/xinput1_4/exports.def @@ -0,0 +1,10 @@ +LIBRARY Xinput1_4 +EXPORTS + DllMain @1 + XInputEnable @5 + XInputGetAudioDeviceIds @10 + XInputGetBatteryInformation @7 + XInputGetCapabilities @4 + XInputGetKeystroke @8 + XInputGetState @2 + XInputSetState @3 \ No newline at end of file diff --git a/xinput1_4/xinput1_4.c b/xinput1_4/xinput1_4.c new file mode 100644 index 0000000..4d3e7aa --- /dev/null +++ b/xinput1_4/xinput1_4.c @@ -0,0 +1,74 @@ +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include +#include "dumbxinputemu.h" + +BOOL APIENTRY DllMain(HANDLE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) +{ + if (ul_reason_for_call == DLL_PROCESS_ATTACH) + dumb_Init(DUMBINPUT_V1_4); + else if (ul_reason_for_call == DLL_PROCESS_DETACH) + dumb_Cleanup(); + return TRUE; +} + +void WINAPI XInputEnable( + _In_ BOOL enable + ){ + dumb_XInputEnable(enable); +} + +DWORD WINAPI XInputGetCapabilities( + _In_ DWORD dwUserIndex, + _In_ DWORD dwFlags, + _Out_ XINPUT_CAPABILITIES *pCapabilities + + ){ + return dumb_XInputGetCapabilities(dwUserIndex, dwFlags, pCapabilities, DUMBINPUT_V1_4); +} + +DWORD WINAPI XInputGetState( + _In_ DWORD dwUserIndex, + _Out_ XINPUT_STATE *pState + + ){ + return dumb_XInputGetState(dwUserIndex, pState, DUMBINPUT_V1_4); +} + +DWORD WINAPI XInputSetState( + _In_ DWORD dwUserIndex, + _Inout_ XINPUT_VIBRATION *pVibration + ){ + return dumb_XInputSetState(dwUserIndex, pVibration, DUMBINPUT_V1_4); +} + +DWORD WINAPI XInputGetKeystroke( + DWORD dwUserIndex, + DWORD dwReserved, + PXINPUT_KEYSTROKE pKeystroke){ + return dumb_XInputGetKeystroke(dwUserIndex, dwReserved, pKeystroke, DUMBINPUT_V1_4); +} + +DWORD WINAPI XInputGetBatteryInformation( + _In_ DWORD dwUserIndex, + _In_ BYTE devType, + _Out_ XINPUT_BATTERY_INFORMATION *pBatteryInformation + ){ + return dumb_XInputGetBatteryInformation(dwUserIndex, devType, pBatteryInformation, DUMBINPUT_V1_4); +} + +DWORD WINAPI XInputGetAudioDeviceIds( + _In_ DWORD dwUserIndex, + _Out_opt_ LPWSTR pRenderDeviceId, + _Inout_opt_ UINT *pRenderCount, + _Out_opt_ LPWSTR pCaptureDeviceId, + _Inout_opt_ UINT *pCaptureCount + ){ + + return dumb_XInputGetAudioDeviceIds( + dwUserIndex, pRenderDeviceId, pRenderCount, pCaptureDeviceId, pCaptureCount, DUMBINPUT_V1_4 + ); +} diff --git a/xinput9_1_0/exports.def b/xinput9_1_0/exports.def new file mode 100644 index 0000000..cbf0990 --- /dev/null +++ b/xinput9_1_0/exports.def @@ -0,0 +1,7 @@ +LIBRARY Xinput9_1_0 +EXPORTS + DllMain @1 + XInputGetCapabilities @2 + XInputGetDSoundAudioDeviceGuids @3 + XInputGetState @4 + XInputSetState @5 diff --git a/xinput9_1_0/xinput9_1_0.c b/xinput9_1_0/xinput9_1_0.c new file mode 100644 index 0000000..67cbf68 --- /dev/null +++ b/xinput9_1_0/xinput9_1_0.c @@ -0,0 +1,48 @@ +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include +#include "dumbxinputemu.h" + +BOOL APIENTRY DllMain(HANDLE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) +{ + if (ul_reason_for_call == DLL_PROCESS_ATTACH) + dumb_Init(DUMBINPUT_V9_1_0); + else if (ul_reason_for_call == DLL_PROCESS_DETACH) + dumb_Cleanup(); + return TRUE; +} + +DWORD WINAPI XInputGetCapabilities( + _In_ DWORD dwUserIndex, + _In_ DWORD dwFlags, + _Out_ XINPUT_CAPABILITIES *pCapabilities + + ){ + return dumb_XInputGetCapabilities(dwUserIndex, dwFlags, pCapabilities, DUMBINPUT_V9_1_0); +} + +DWORD WINAPI XInputGetDSoundAudioDeviceGuids( + DWORD dwUserIndex, + GUID* pDSoundRenderGuid, + GUID* pDSoundCaptureGuid + ){ + return dumb_XInputGetDSoundAudioDeviceGuids(dwUserIndex, pDSoundRenderGuid, pDSoundCaptureGuid, DUMBINPUT_V9_1_0); +} + +DWORD WINAPI XInputGetState( + _In_ DWORD dwUserIndex, + _Out_ XINPUT_STATE *pState + + ){ + return dumb_XInputGetState(dwUserIndex, pState, DUMBINPUT_V9_1_0); +} + +DWORD WINAPI XInputSetState( + _In_ DWORD dwUserIndex, + _Inout_ XINPUT_VIBRATION *pVibration + ){ + return dumb_XInputSetState(dwUserIndex, pVibration, DUMBINPUT_V9_1_0); +} \ No newline at end of file diff --git a/xinput_obiwan.patch b/xinput_obiwan.patch new file mode 100644 index 0000000..0331e80 --- /dev/null +++ b/xinput_obiwan.patch @@ -0,0 +1,588 @@ +diff --git xinput1_3/Makefile.in xinput1_3/Makefile.in +index cf8f730..37621fa 100644 +--- xinput1_3/Makefile.in ++++ xinput1_3/Makefile.in +@@ -1,5 +1,6 @@ + MODULE = xinput1_3.dll + IMPORTLIB = xinput ++IMPORTS = uuid dxguid dinput dinput8 ole32 + + C_SRCS = \ + xinput1_3_main.c +diff --git xinput1_3/xinput1_3_main.c xinput1_3/xinput1_3_main.c +index 63f725b..f16f56a 100644 +--- xinput1_3/xinput1_3_main.c ++++ xinput1_3/xinput1_3_main.c +@@ -1,6 +1,8 @@ + /* + * The Wine project - Xinput Joystick Library ++ * + * Copyright 2008 Andrew Fenn ++ * Copyright 2016 Bruno Jesus + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public +@@ -17,6 +19,7 @@ + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + ++#define COBJMACROS + #include "config.h" + #include + #include +@@ -27,10 +30,342 @@ + #include "winbase.h" + #include "winerror.h" + ++#include "initguid.h" + #include "xinput.h" ++#include "dinput.h" ++#include "dinputd.h" + + WINE_DEFAULT_DEBUG_CHANNEL(xinput); + ++struct CapsFlags ++{ ++ BOOL wireless, jedi, pov; ++ int axes, buttons; ++}; ++ ++static struct ControllerMap ++{ ++ LPDIRECTINPUTDEVICE8A device; ++ BOOL connected, acquired; ++ struct CapsFlags caps; ++ XINPUT_STATE state; ++ XINPUT_VIBRATION vibration; ++ BOOL vibration_dirty; ++ ++ DIEFFECT effect_data; ++ LPDIRECTINPUTEFFECT effect_instance; ++} controllers[XUSER_MAX_COUNT]; ++ ++static struct ++{ ++ LPDIRECTINPUT8A iface; ++ BOOL enabled; ++ int mapped; ++} dinput; ++ ++#define STARTUP_DINPUT if (!dinput.iface) dinput_start(); ++ ++/* ========================= Internal functions ============================= */ ++ ++static BOOL dinput_is_good(const LPDIRECTINPUTDEVICE8A device, struct CapsFlags *caps) ++{ ++ HRESULT hr; ++ DIPROPDWORD property; ++ DIDEVCAPS dinput_caps; ++ static const unsigned long wireless_products[] = { ++ MAKELONG(0x045e, 0x0291) /* microsoft receiver */, ++ MAKELONG(0x045e, 0x0719) /* microsoft controller */, ++ MAKELONG(0x0738, 0x4556) /* mad catz */, ++ MAKELONG(0x0e6f, 0x0003) /* logitech */, ++ MAKELONG(0x0e6f, 0x0005) /* eclipse */, ++ MAKELONG(0x0e6f, 0x0006) /* edge */, ++ MAKELONG(0x102c, 0xff0c) /* joytech */ ++ }; ++ int i; ++ ++ dinput_caps.dwSize = sizeof(dinput_caps); ++ hr = IDirectInputDevice_GetCapabilities(device, &dinput_caps); ++ if (FAILED(hr)) ++ return FALSE; ++ ++ property.diph.dwSize = sizeof(property); ++ property.diph.dwHeaderSize = sizeof(property.diph); ++ property.diph.dwObj = 0; ++ property.diph.dwHow = DIPH_DEVICE; ++ ++ hr = IDirectInputDevice_GetProperty(device, DIPROP_VIDPID, &property.diph); ++ if (FAILED(hr)) ++ return FALSE; ++ ++ if (dinput_caps.dwAxes < 2 || dinput_caps.dwButtons < 8) ++ return FALSE; ++ ++ caps->axes = dinput_caps.dwAxes; ++ caps->buttons = dinput_caps.dwButtons; ++ caps->wireless = FALSE; ++ caps->jedi = !!(dinput_caps.dwFlags & DIDC_FORCEFEEDBACK); ++ caps->pov = !!dinput_caps.dwPOVs; ++ ++ for (i = 0; i < sizeof(wireless_products) / sizeof(wireless_products[0]); i++) ++ if (property.dwData == wireless_products[i]) ++ { ++ caps->wireless = TRUE; ++ break; ++ } ++ ++ if (dinput_caps.dwAxes == 6 && dinput_caps.dwButtons == 11 && dinput_caps.dwPOVs == 1) ++ TRACE("This controller has the same number of buttons/axes from xbox 360, should work...\n"); ++ else ++ FIXME("This is not a known xbox controller, using anyway. Expect problems!\n"); ++ ++ return TRUE; ++} ++ ++static BOOL dinput_set_range(const LPDIRECTINPUTDEVICE8A device) ++{ ++ HRESULT hr; ++ DIPROPRANGE property; ++ ++ property.diph.dwSize = sizeof(property); ++ property.diph.dwHeaderSize = sizeof(property.diph); ++ property.diph.dwHow = DIPH_DEVICE; ++ property.diph.dwObj = 0; ++ property.lMin = -32767; ++ property.lMax = +32767; ++ ++ hr = IDirectInputDevice_SetProperty(device, DIPROP_RANGE, &property.diph); ++ if (FAILED(hr)) ++ { ++ WARN("Failed to set axis range (0x%x)\n", hr); ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++static void dinput_joystate_to_xinput(DIJOYSTATE2 *js, XINPUT_GAMEPAD *gamepad, struct CapsFlags *caps) ++{ ++ static const int xbox_buttons[] = { ++ XINPUT_GAMEPAD_A, ++ XINPUT_GAMEPAD_B, ++ XINPUT_GAMEPAD_X, ++ XINPUT_GAMEPAD_Y, ++ XINPUT_GAMEPAD_LEFT_SHOULDER, ++ XINPUT_GAMEPAD_RIGHT_SHOULDER, ++ XINPUT_GAMEPAD_BACK, ++ XINPUT_GAMEPAD_START, ++ 0, /* xbox key not used */ ++ XINPUT_GAMEPAD_LEFT_THUMB, ++ XINPUT_GAMEPAD_RIGHT_THUMB ++ }; ++ int i, buttons; ++ ++ gamepad->wButtons = 0x0000; ++ /* First the D-Pad which is recognized as a POV in dinput */ ++ if (caps->pov) ++ { ++ switch (js->rgdwPOV[0]) ++ { ++ case 0 : gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_UP; break; ++ case 4500 : gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_UP; /* fall through */ ++ case 9000 : gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT; break; ++ case 13500: gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT; /* fall through */ ++ case 18000: gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_DOWN; break; ++ case 22500: gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_DOWN; /* fall through */ ++ case 27000: gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_LEFT; break; ++ case 31500: gamepad->wButtons |= XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_DPAD_UP; ++ } ++ } ++ ++ /* Buttons */ ++ buttons = min(caps->buttons, sizeof(xbox_buttons) / sizeof(*xbox_buttons)); ++ for (i = 0; i < buttons; i++) ++ if (js->rgbButtons[i] & 0x80) ++ gamepad->wButtons |= xbox_buttons[i]; ++ ++ /* Axes */ ++ gamepad->sThumbLX = js->lX; ++ gamepad->sThumbLY = -js->lY; ++ if (caps->axes >= 4) ++ { ++ gamepad->sThumbRX = js->lRx; ++ gamepad->sThumbRY = -js->lRy; ++ } ++ else ++ gamepad->sThumbRX = gamepad->sThumbRY = 0; ++ ++ /* Both triggers */ ++ if (caps->axes >= 6) ++ { ++ gamepad->bLeftTrigger = (255 * (js->lZ + 32767)) / 32767; ++ gamepad->bRightTrigger = (255 * (js->lRz + 32767)) / 32767; ++ } ++ else ++ gamepad->bLeftTrigger = gamepad->bRightTrigger = 0; ++} ++ ++static void dinput_fill_effect(DIEFFECT *effect) ++{ ++ static DWORD axes[2] = {DIJOFS_X, DIJOFS_Y}; ++ static LONG direction[2] = {0, 0}; ++ ++ effect->dwSize = sizeof(effect); ++ effect->dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; ++ effect->dwDuration = INFINITE; ++ effect->dwGain = 0; ++ effect->dwTriggerButton = DIEB_NOTRIGGER; ++ effect->cAxes = sizeof(axes) / sizeof(axes[0]); ++ effect->rgdwAxes = axes; ++ effect->rglDirection = direction; ++} ++ ++static void dinput_send_effect(int index, int power) ++{ ++ HRESULT hr; ++ DIPERIODIC periodic; ++ DIEFFECT *effect = &controllers[index].effect_data; ++ LPDIRECTINPUTEFFECT *instance = &controllers[index].effect_instance; ++ ++ if (!*instance) ++ dinput_fill_effect(effect); ++ ++ effect->cbTypeSpecificParams = sizeof(periodic); ++ effect->lpvTypeSpecificParams = &periodic; ++ ++ periodic.dwMagnitude = power; ++ periodic.dwPeriod = DI_SECONDS; /* 1 second */ ++ periodic.lOffset = 0; ++ periodic.dwPhase = 0; ++ ++ if (!*instance) ++ { ++ hr = IDirectInputDevice8_CreateEffect(controllers[index].device, &GUID_Square, ++ effect, instance, NULL); ++ if (FAILED(hr)) ++ { ++ WARN("Failed to create effect (0x%x)\n", hr); ++ return; ++ } ++ if (!*instance) ++ { ++ WARN("Effect not returned???\n"); ++ return; ++ } ++ ++ hr = IDirectInputEffect_SetParameters(*instance, effect, DIEP_AXES | DIEP_DIRECTION | DIEP_NODOWNLOAD); ++ if (FAILED(hr)) ++ { ++ IUnknown_Release(*instance); ++ *instance = NULL; ++ WARN("Failed to configure effect (0x%x)\n", hr); ++ return; ++ } ++ } ++ ++ hr = IDirectInputEffect_SetParameters(*instance, effect, DIEP_TYPESPECIFICPARAMS | DIEP_START); ++ if (FAILED(hr)) ++ { ++ WARN("Failed to play effect (0x%x)\n", hr); ++ return; ++ } ++} ++ ++static BOOL CALLBACK dinput_enum_callback(const DIDEVICEINSTANCEA *instance, void *context) ++{ ++ LPDIRECTINPUTDEVICE8A device; ++ HRESULT hr; ++ ++ if (dinput.mapped == sizeof(controllers) / sizeof(*controllers)) ++ return DIENUM_STOP; ++ ++ hr = IDirectInput_CreateDevice(dinput.iface, &instance->guidInstance, &device, NULL); ++ if (FAILED(hr)) ++ return DIENUM_CONTINUE; ++ ++ if (!dinput_is_good(device, &controllers[dinput.mapped].caps)) ++ { ++ IDirectInput_Release(device); ++ return DIENUM_CONTINUE; ++ } ++ ++ if (!dinput_set_range(device)) ++ { ++ IDirectInput_Release(device); ++ return DIENUM_CONTINUE; ++ } ++ ++ controllers[dinput.mapped].connected = TRUE; ++ controllers[dinput.mapped].device = device; ++ dinput.mapped++; ++ ++ return DIENUM_CONTINUE; ++} ++ ++static void dinput_start(void) ++{ ++ HRESULT hr; ++ ++ hr = DirectInput8Create(GetModuleHandleA(NULL), 0x0800, &IID_IDirectInput8A, ++ (void **)&dinput.iface, NULL); ++ if (FAILED(hr)) ++ { ++ ERR("Failed to create dinput8 interface, no xinput controller support (0x%x)\n", hr); ++ return; ++ } ++ ++ hr = IDirectInput8_EnumDevices(dinput.iface, DI8DEVCLASS_GAMECTRL, ++ dinput_enum_callback, NULL, DIEDFL_ATTACHEDONLY); ++ if (FAILED(hr)) ++ { ++ ERR("Failed to enumerate dinput8 devices, no xinput controller support (0x%x)\n", hr); ++ return; ++ } ++ ++ dinput.enabled = TRUE; ++} ++ ++static void dinput_update(int index) ++{ ++ HRESULT hr; ++ DIJOYSTATE2 data; ++ XINPUT_GAMEPAD gamepad; ++ ++ if (dinput.enabled) ++ { ++ if (!controllers[index].acquired) ++ { ++ IDirectInputDevice8_SetDataFormat(controllers[index].device, &c_dfDIJoystick2); ++ hr = IDirectInputDevice8_Acquire(controllers[index].device); ++ if (FAILED(hr)) ++ { ++ WARN("Failed to acquire game controller (0x%x)\n", hr); ++ return; ++ } ++ controllers[index].acquired = TRUE; ++ } ++ ++ IDirectInputDevice8_Poll(controllers[index].device); ++ hr = IDirectInputDevice_GetDeviceState(controllers[index].device, sizeof(data), &data); ++ if (FAILED(hr)) ++ { ++ if (hr == DIERR_INPUTLOST) ++ controllers[index].acquired = FALSE; ++ WARN("Failed to get game controller state (0x%x)\n", hr); ++ return; ++ } ++ dinput_joystate_to_xinput(&data, &gamepad, &controllers[index].caps); ++ } ++ else ++ memset(&gamepad, 0, sizeof(gamepad)); ++ ++ if (memcmp(&controllers[index].state.Gamepad, &gamepad, sizeof(gamepad))) ++ { ++ controllers[index].state.Gamepad = gamepad; ++ controllers[index].state.dwPacketNumber++; ++ } ++} ++ ++/* ============================ Dll Functions =============================== */ ++ + BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) + { + switch(reason) +@@ -46,87 +381,163 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) + + void WINAPI XInputEnable(BOOL enable) + { +- /* Setting to false will stop messages from XInputSetState being sent +- to the controllers. Setting to true will send the last vibration +- value (sent to XInputSetState) to the controller and allow messages to +- be sent */ +- FIXME("(%d) Stub!\n", enable); ++ TRACE("(%d)\n", enable); ++ ++ STARTUP_DINPUT ++ ++ if((dinput.enabled = enable)) ++ { ++ int i; ++ /* Apply the last vibration status that was sent to the controller ++ * while xinput was disabled. */ ++ for (i = 0; i < sizeof(controllers) / sizeof(*controllers); i++) ++ { ++ if (controllers[i].connected && controllers[i].vibration_dirty) ++ XInputSetState(i, &controllers[i].vibration); ++ } ++ } + } + +-DWORD WINAPI XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration) ++DWORD WINAPI XInputSetState(DWORD index, XINPUT_VIBRATION* vibration) + { +- FIXME("(%d %p) Stub!\n", dwUserIndex, pVibration); ++ TRACE("(%u %p)\n", index, vibration); + +- if (dwUserIndex < XUSER_MAX_COUNT) +- { ++ STARTUP_DINPUT ++ ++ if (index >= XUSER_MAX_COUNT) ++ return ERROR_BAD_ARGUMENTS; ++ if (!controllers[index].connected) + return ERROR_DEVICE_NOT_CONNECTED; +- /* If controller exists then return ERROR_SUCCESS */ ++ ++ /* Check if we really have to do all the process */ ++ if (!controllers[index].vibration_dirty && ++ !memcmp(&controllers[index].vibration, vibration, sizeof(*vibration))) ++ return ERROR_SUCCESS; ++ ++ controllers[index].vibration = *vibration; ++ controllers[index].vibration_dirty = !dinput.enabled; ++ ++ if (dinput.enabled && controllers[index].caps.jedi) ++ { ++ int power; ++ /* FIXME: we can't set the speed of each motor so do an average */ ++ power = DI_FFNOMINALMAX * (vibration->wLeftMotorSpeed + vibration->wRightMotorSpeed) / 2 / 0xFFFF; ++ ++ TRACE("Vibration left/right speed %d/%d translated to %d\n\n", ++ vibration->wLeftMotorSpeed, vibration->wRightMotorSpeed, power); ++ dinput_send_effect(index, power); + } +- return ERROR_BAD_ARGUMENTS; ++ ++ return ERROR_SUCCESS; + } + +-DWORD WINAPI DECLSPEC_HOTPATCH XInputGetState(DWORD dwUserIndex, XINPUT_STATE* pState) ++DWORD WINAPI DECLSPEC_HOTPATCH XInputGetState(DWORD index, XINPUT_STATE* state) + { +- static int warn_once; ++ TRACE("(%u %p)\n", index, state); + +- if (!warn_once++) +- FIXME("(%u %p)\n", dwUserIndex, pState); ++ STARTUP_DINPUT + +- if (dwUserIndex < XUSER_MAX_COUNT) +- { ++ if (index >= XUSER_MAX_COUNT) ++ return ERROR_BAD_ARGUMENTS; ++ if (!controllers[index].connected) + return ERROR_DEVICE_NOT_CONNECTED; +- /* If controller exists then return ERROR_SUCCESS */ +- } +- return ERROR_BAD_ARGUMENTS; ++ ++ dinput_update(index); ++ *state = controllers[index].state; ++ ++ return ERROR_SUCCESS; + } + +-DWORD WINAPI XInputGetKeystroke(DWORD dwUserIndex, DWORD dwReserve, PXINPUT_KEYSTROKE pKeystroke) ++DWORD WINAPI XInputGetKeystroke(DWORD index, DWORD reserved, PXINPUT_KEYSTROKE key) + { +- FIXME("(%d %d %p) Stub!\n", dwUserIndex, dwReserve, pKeystroke); ++ TRACE("(%u %d %p) stub!\n", index, reserved, key); + +- if (dwUserIndex < XUSER_MAX_COUNT) +- { ++ STARTUP_DINPUT ++ ++ if (index >= XUSER_MAX_COUNT) ++ return ERROR_BAD_ARGUMENTS; ++ if (!controllers[index].connected) + return ERROR_DEVICE_NOT_CONNECTED; +- /* If controller exists then return ERROR_SUCCESS */ +- } +- return ERROR_BAD_ARGUMENTS; ++ ++ return ERROR_NOT_SUPPORTED; + } + +-DWORD WINAPI XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES* pCapabilities) ++/* Not defined anywhere ??? */ ++#define XINPUT_CAPS_FFB_SUPPORTED 0x0001 ++#define XINPUT_CAPS_WIRELESS 0x0002 ++#define XINPUT_CAPS_NO_NAVIGATION 0x0010 ++ ++DWORD WINAPI XInputGetCapabilities(DWORD index, DWORD flags, XINPUT_CAPABILITIES* capabilities) + { +- static int warn_once; ++ TRACE("(%u %d %p)\n", index, flags, capabilities); + +- if (!warn_once++) +- FIXME("(%d %d %p)\n", dwUserIndex, dwFlags, pCapabilities); ++ STARTUP_DINPUT + +- if (dwUserIndex < XUSER_MAX_COUNT) +- { ++ if (index >= XUSER_MAX_COUNT || (flags && (flags & ~XINPUT_FLAG_GAMEPAD))) ++ return ERROR_BAD_ARGUMENTS; ++ if (!controllers[index].connected) + return ERROR_DEVICE_NOT_CONNECTED; +- /* If controller exists then return ERROR_SUCCESS */ +- } +- return ERROR_BAD_ARGUMENTS; ++ ++ capabilities->Type = XINPUT_DEVTYPE_GAMEPAD; ++ capabilities->SubType = XINPUT_DEVSUBTYPE_GAMEPAD; ++ ++ capabilities->Flags = 0; ++ if (controllers[index].caps.jedi) ++ capabilities->Flags |= XINPUT_CAPS_FFB_SUPPORTED; ++ if (controllers[index].caps.wireless) ++ capabilities->Flags |= XINPUT_CAPS_WIRELESS; ++ if (!controllers[index].caps.pov) ++ capabilities->Flags |= XINPUT_CAPS_NO_NAVIGATION; ++ ++ dinput_update(index); ++ ++ capabilities->Vibration = controllers[index].vibration; ++ capabilities->Gamepad = controllers[index].state.Gamepad; ++ ++ return ERROR_SUCCESS; + } + +-DWORD WINAPI XInputGetDSoundAudioDeviceGuids(DWORD dwUserIndex, GUID* pDSoundRenderGuid, GUID* pDSoundCaptureGuid) ++DWORD WINAPI XInputGetDSoundAudioDeviceGuids(DWORD index, GUID* dsound_render_guid, GUID* dsound_capture_guid) + { +- FIXME("(%d %p %p) Stub!\n", dwUserIndex, pDSoundRenderGuid, pDSoundCaptureGuid); ++ TRACE("(%u %p %p) Stub!\n", index, dsound_render_guid, dsound_capture_guid); + +- if (dwUserIndex < XUSER_MAX_COUNT) +- { ++ STARTUP_DINPUT ++ ++ if (index >= XUSER_MAX_COUNT) ++ return ERROR_BAD_ARGUMENTS; ++ if (!controllers[index].connected) + return ERROR_DEVICE_NOT_CONNECTED; +- /* If controller exists then return ERROR_SUCCESS */ +- } +- return ERROR_BAD_ARGUMENTS; ++ ++ return ERROR_NOT_SUPPORTED; + } + +-DWORD WINAPI XInputGetBatteryInformation(DWORD dwUserIndex, BYTE deviceType, XINPUT_BATTERY_INFORMATION* pBatteryInfo) ++DWORD WINAPI XInputGetBatteryInformation(DWORD index, BYTE type, XINPUT_BATTERY_INFORMATION* battery) + { +- FIXME("(%d %u %p) Stub!\n", dwUserIndex, deviceType, pBatteryInfo); ++ TRACE("(%u %u %p) Stub!\n", index, type, battery); + +- if (dwUserIndex < XUSER_MAX_COUNT) +- { ++ STARTUP_DINPUT ++ ++ if (index >= XUSER_MAX_COUNT) ++ return ERROR_BAD_ARGUMENTS; ++ if (!controllers[index].connected) + return ERROR_DEVICE_NOT_CONNECTED; +- /* If controller exists then return ERROR_SUCCESS */ ++ if (type != BATTERY_DEVTYPE_GAMEPAD && type != BATTERY_DEVTYPE_HEADSET) ++ return ERROR_BAD_ARGUMENTS; ++ ++ if (!controllers[index].caps.wireless) ++ { ++ battery->BatteryType = BATTERY_TYPE_WIRED; ++ battery->BatteryLevel = BATTERY_LEVEL_FULL; + } +- return ERROR_BAD_ARGUMENTS; ++ else ++ { ++ static int once; ++ if (!once++) ++ FIXME("Reporting fake battery values\n"); ++ ++ battery->BatteryType = BATTERY_TYPE_NIMH; ++ battery->BatteryLevel = BATTERY_LEVEL_MEDIUM; ++ } ++ ++ return ERROR_SUCCESS; + }