From 85f58cdd2eab71c3b0453e2bff41b47a6cb98a9e Mon Sep 17 00:00:00 2001 From: Beren Minor Date: Sat, 1 Jul 2017 14:28:06 +0200 Subject: [PATCH 1/5] Cleanup CMakeLists, use modern CMake properties for C++11 --- CMakeLists.txt | 49 +++++++++++-------------------------------------- 1 file changed, 11 insertions(+), 38 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c9e0cc..aea0349 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,44 +1,17 @@ -PROJECT(koku-xinput-wine) -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.6) +project(koku-xinput-wine) -# Check for C++11 -include(CheckCXXCompilerFlag) -CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) -CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) -if(COMPILER_SUPPORTS_CXX11) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") -elseif(COMPILER_SUPPORTS_CXX0X) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") -else() - message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") -endif() - -# Check for 32bit version option(BUILD_M32 "Build library in 32Bit mode" ON) if(BUILD_M32) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") + add_compile_options(-m32) endif() -ADD_LIBRARY(koku-xinput-wine SHARED main.cpp xinput.cpp device.cpp) - -# Pull in pkgconfig -include(FindPkgConfig) - -# - Try to find SDL -# Once done this will define -# -# SDL_FOUND - system has libSDL -# SDL_INCLUDE_DIRS - the libSDL include directory -# SDL_LIBRARIES - The libSDL libraries -if(PKG_CONFIG_FOUND) - pkg_check_modules (SDL2 REQUIRED sdl2) -else() - find_package(SDL2 REQURIED) -endif() - -include_directories(${SDL2_INCLUDE_DIR}) - -# Link SDL -target_link_libraries(koku-xinput-wine ${SDL2_LIBRARIES}) -set_target_properties(koku-xinput-wine PROPERTIES PREFIX "") +add_library(koku-xinput-wine SHARED main.h main.cpp xinput.h xinput.cpp + device.h device.cpp) +set_target_properties(koku-xinput-wine PROPERTIES CXX_STANDARD 11 PREFIX "") +find_package(PkgConfig REQUIRED) +pkg_check_modules(SDL2 REQUIRED sdl2) +target_include_directories(koku-xinput-wine PRIVATE ${SDL2_INCLUDE_DIR}) +target_link_libraries(koku-xinput-wine PRIVATE ${SDL2_LIBRARIES}) From 9c368f57777dabd85baeac3177c6a008d6912b6c Mon Sep 17 00:00:00 2001 From: Beren Minor Date: Sat, 1 Jul 2017 14:33:12 +0200 Subject: [PATCH 2/5] Add test sources and build them using mingw64 if available --- CMakeLists.txt | 56 +++-- Toolchain-Windows-i686.cmake | 12 ++ Toolchain-Windows-x86_64.cmake | 12 ++ test/ditest.cpp | 362 +++++++++++++++++++++++++++++++++ test/xitest.cpp | 6 + 5 files changed, 436 insertions(+), 12 deletions(-) create mode 100644 Toolchain-Windows-i686.cmake create mode 100644 Toolchain-Windows-x86_64.cmake create mode 100644 test/ditest.cpp create mode 100644 test/xitest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index aea0349..b7111a8 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,17 +1,49 @@ cmake_minimum_required(VERSION 3.6) project(koku-xinput-wine) -option(BUILD_M32 "Build library in 32Bit mode" ON) -if(BUILD_M32) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") - add_compile_options(-m32) -endif() +if (NOT CMAKE_C_COMPILER MATCHES "i686-w64-mingw32-gcc" + AND NOT CMAKE_C_COMPILER MATCHES "x86_64-w64-mingw32-gcc") + find_program(X86_64_MINGW64_GCC x86_64-w64-mingw32-gcc) + if (X86_64_MINGW64_GCC) + execute_process( + COMMAND ${CMAKE_COMMAND} + -B${CMAKE_BINARY_DIR}/x86_64-w64-mingw32 + -H${CMAKE_SOURCE_DIR} + -DCMAKE_TOOLCHAIN_FILE=${CMAKE_SOURCE_DIR}/Toolchain-Windows-x86_64.cmake) + add_custom_target(x86_64-w64-mingw32-tests ALL + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/x86_64-w64-mingw32) + endif() + + find_program(I686_MINGW64_GCC i686-w64-mingw32-gcc) + if (I686_MINGW64_GCC) + execute_process( + COMMAND ${CMAKE_COMMAND} + -B${CMAKE_BINARY_DIR}/i686-w64-mingw32 + -H${CMAKE_SOURCE_DIR} + -DCMAKE_TOOLCHAIN_FILE=${CMAKE_SOURCE_DIR}/Toolchain-Windows-i686.cmake) + add_custom_target(i686-w64-mingw32-tests ALL + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}/i686-w64-mingw32) + endif() -add_library(koku-xinput-wine SHARED main.h main.cpp xinput.h xinput.cpp - device.h device.cpp) -set_target_properties(koku-xinput-wine PROPERTIES CXX_STANDARD 11 PREFIX "") + option(BUILD_M32 "Build library in 32Bit mode" ON) + if(BUILD_M32) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") + add_compile_options(-m32) + endif() -find_package(PkgConfig REQUIRED) -pkg_check_modules(SDL2 REQUIRED sdl2) -target_include_directories(koku-xinput-wine PRIVATE ${SDL2_INCLUDE_DIR}) -target_link_libraries(koku-xinput-wine PRIVATE ${SDL2_LIBRARIES}) + add_library(koku-xinput-wine SHARED main.h main.cpp xinput.h xinput.cpp + device.h device.cpp) + set_target_properties(koku-xinput-wine PROPERTIES CXX_STANDARD 11 PREFIX "") + + find_package(PkgConfig REQUIRED) + pkg_check_modules(SDL2 REQUIRED sdl2) + target_include_directories(koku-xinput-wine PRIVATE ${SDL2_INCLUDE_DIR}) + target_link_libraries(koku-xinput-wine PRIVATE ${SDL2_LIBRARIES}) +else() + add_executable(ditest test/ditest.cpp) + target_link_libraries(ditest PRIVATE -static -static-libgcc + dinput dinput8 dxguid user32 ole32 oleaut32) + add_executable(xitest test/xitest.cpp) + target_link_libraries(xitest PRIVATE -static -static-libgcc + xinput) +endif() diff --git a/Toolchain-Windows-i686.cmake b/Toolchain-Windows-i686.cmake new file mode 100644 index 0000000..bc66f63 --- /dev/null +++ b/Toolchain-Windows-i686.cmake @@ -0,0 +1,12 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(TOOLCHAIN_PREFIX i686-w64-mingw32) + +set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) +set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) +set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) + +set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/Toolchain-Windows-x86_64.cmake b/Toolchain-Windows-x86_64.cmake new file mode 100644 index 0000000..854d17f --- /dev/null +++ b/Toolchain-Windows-x86_64.cmake @@ -0,0 +1,12 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(TOOLCHAIN_PREFIX x86_64-w64-mingw32) + +set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) +set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) +set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) + +set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/test/ditest.cpp b/test/ditest.cpp new file mode 100644 index 0000000..c805310 --- /dev/null +++ b/test/ditest.cpp @@ -0,0 +1,362 @@ +#include + +#include +#include +#include +#include + +#include +#include +// #include + +#ifndef SAFE_RELEASE +#define SAFE_RELEASE(x) \ + if (x != NULL) { \ + x->Release(); \ + x = NULL; \ + } +#endif + +//----------------------------------------------------------------------------- +// Enum each PNP device using WMI and check each device ID to see if it contains +// "IG_" (ex. "VID_045E&PID_028E&IG_00"). If it does, then it's an XInput +// device Unfortunately this information can not be found by just using +// DirectInput +//----------------------------------------------------------------------------- +BOOL IsXInputDevice(const GUID *pGuidProductFromDirectInput) { + IWbemLocator *pIWbemLocator = NULL; + IEnumWbemClassObject *pEnumDevices = NULL; + IWbemClassObject *pDevices[20] = {0}; + IWbemServices *pIWbemServices = NULL; + BSTR bstrNamespace = NULL; + BSTR bstrDeviceID = NULL; + BSTR bstrClassName = NULL; + DWORD uReturned = 0; + bool bIsXinputDevice = false; + UINT iDevice = 0; + VARIANT var; + HRESULT hr; + + // CoInit if needed + hr = CoInitialize(NULL); + bool bCleanupCOM = SUCCEEDED(hr); + + // Create WMI + hr = CoCreateInstance(__uuidof(WbemLocator), NULL, CLSCTX_INPROC_SERVER, + __uuidof(IWbemLocator), (LPVOID *)&pIWbemLocator); + if (FAILED(hr) || pIWbemLocator == NULL) + goto LCleanup; + + bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2"); + if (bstrNamespace == NULL) + goto LCleanup; + bstrClassName = SysAllocString(L"Win32_PNPEntity"); + if (bstrClassName == NULL) + goto LCleanup; + bstrDeviceID = SysAllocString(L"DeviceID"); + if (bstrDeviceID == NULL) + goto LCleanup; + + // Connect to WMI + hr = pIWbemLocator->ConnectServer(bstrNamespace, NULL, NULL, 0L, 0L, NULL, + NULL, &pIWbemServices); + if (FAILED(hr) || pIWbemServices == NULL) + goto LCleanup; + + // Switch security level to IMPERSONATE. + CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, + RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, + EOAC_NONE); + + hr = + pIWbemServices->CreateInstanceEnum(bstrClassName, 0, NULL, &pEnumDevices); + if (FAILED(hr) || pEnumDevices == NULL) + goto LCleanup; + + // Loop over all devices + for (;;) { + // Get 20 at a time + hr = pEnumDevices->Next(10000, 20, pDevices, &uReturned); + if (FAILED(hr)) + goto LCleanup; + if (uReturned == 0) + break; + + for (iDevice = 0; iDevice < uReturned; iDevice++) { + // For each device, get its device ID + hr = pDevices[iDevice]->Get(bstrDeviceID, 0L, &var, NULL, NULL); + if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL) { + // Check if the device ID contains "IG_". If it does, then it's an + // XInput device This information can not be found from DirectInput + if (wcsstr(var.bstrVal, L"IG_")) { + // If it does, then get the VID/PID from var.bstrVal + DWORD dwPid = 0, dwVid = 0; + WCHAR *strVid = wcsstr(var.bstrVal, L"VID_"); + if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1) + dwVid = 0; + WCHAR *strPid = wcsstr(var.bstrVal, L"PID_"); + if (strPid && swscanf(strPid, L"PID_%4X", &dwPid) != 1) + dwPid = 0; + + // Compare the VID/PID to the DInput device + DWORD dwVidPid = MAKELONG(dwVid, dwPid); + printf("Found device %04x\n", pGuidProductFromDirectInput->Data1); + if (dwVidPid == pGuidProductFromDirectInput->Data1) { + printf("Found XINPUT device %04x\n", + pGuidProductFromDirectInput->Data1); + bIsXinputDevice = true; + goto LCleanup; + } + } + } + SAFE_RELEASE(pDevices[iDevice]); + } + } + +LCleanup: + if (bstrNamespace) + SysFreeString(bstrNamespace); + if (bstrDeviceID) + SysFreeString(bstrDeviceID); + if (bstrClassName) + SysFreeString(bstrClassName); + for (iDevice = 0; iDevice < 20; iDevice++) + SAFE_RELEASE(pDevices[iDevice]); + SAFE_RELEASE(pEnumDevices); + SAFE_RELEASE(pIWbemLocator); + SAFE_RELEASE(pIWbemServices); + + if (bCleanupCOM) + CoUninitialize(); + + return bIsXinputDevice; +} + +LPDIRECTINPUT8 g_pDI; +LPDIRECTINPUTDEVICE8 g_pJoystick; + +//----------------------------------------------------------------------------- +// Name: EnumJoysticksCallback() +// Desc: Called once for each enumerated joystick. If we find one, create a +// device interface on it so we can play with it. +//----------------------------------------------------------------------------- +BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE *pdidInstance, + VOID *pContext) { + HRESULT hr; + + if (IsXInputDevice(&pdidInstance->guidProduct)) + return DIENUM_CONTINUE; + + // Device is verified not XInput, so add it to the list of DInput devices + + hr = g_pDI->CreateDevice(pdidInstance->guidInstance, &g_pJoystick, NULL); + return DIENUM_CONTINUE; +} + +BOOL CALLBACK EnumAxesCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef) { + DIPROPRANGE range; + range.diph.dwSize = sizeof(DIPROPRANGE); + range.diph.dwHeaderSize = sizeof(DIPROPHEADER); + range.diph.dwHow = DIPH_BYID; + range.diph.dwObj = lpddoi->dwType; + range.lMin = -1000; + range.lMax = +1000; + + HRESULT hr = g_pJoystick->SetProperty(DIPROP_RANGE, &range.diph); + if (FAILED(hr)) + return DIENUM_STOP; + + return DIENUM_CONTINUE; +} + +void shutdown() { + if (g_pJoystick) + g_pJoystick->Release(); + + if (g_pDI) + g_pDI->Release(); +} + +int main() { + HRESULT hr; + + hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, + IID_IDirectInput8, (void **)&g_pDI, NULL); + if (FAILED(hr)) + return 10; + + printf("DirectInput initialized.\n"); + + g_pDI->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, NULL, + DIEDFL_ATTACHEDONLY); + + if (!g_pDI) { + printf("Couldn't find a joystick.\n"); + shutdown(); + return 10; + } + + printf("Joystick created.\n"); + + if (FAILED(hr = g_pJoystick->SetDataFormat(&c_dfDIJoystick2))) { + printf("Failed to set joystick data format.\n"); + shutdown(); + return 10; + } + + WNDCLASS wc; + + wc.style = 0; + wc.lpfnWndProc = DefWindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = GetModuleHandle(NULL); + wc.hIcon = NULL; + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = "foo"; + + HWND hwnd = CreateWindow((LPCTSTR)RegisterClass(&wc), "", WS_POPUP, 0, 0, 0, + 0, NULL, NULL, wc.hInstance, NULL); + + if (FAILED(hr = g_pJoystick->SetCooperativeLevel( + hwnd, DISCL_EXCLUSIVE | DISCL_BACKGROUND))) { + printf("Failed to set cooperative level.\n"); + shutdown(); + return 10; + } + + if (FAILED( + hr = g_pJoystick->EnumObjects(EnumAxesCallback, NULL, DIDFT_AXIS))) { + printf("Failed to enumerate axes.\n"); + shutdown(); + return 10; + } + + printf("All OK!\n"); + + bool acq = false; + + int winfx = 800 << 16; + int winfy = 600 << 16; + int winx = 0; + int winy = 0; + int acscale = 0; + bool left = false, right = false, abs = false; + bool absmode = false; + + for (;;) { + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + Sleep(10); + + hr = S_OK; + if (!acq) { + hr = g_pJoystick->Acquire(); + acq = SUCCEEDED(hr); + if (FAILED(hr)) { + printf("failed to acquire! %08x\n", hr); + } + } + if (SUCCEEDED(hr)) { + hr = g_pJoystick->Poll(); + if (FAILED(hr)) + printf("poll failed!\n"); + } + if (FAILED(hr)) { + hr = g_pJoystick->Acquire(); + while (hr == DIERR_INPUTLOST) + hr = g_pJoystick->Acquire(); + if (FAILED(hr)) + acq = false; + else + acq = true; + } + + if (SUCCEEDED(hr)) { + DIJOYSTATE2 js; + HRESULT hr = g_pJoystick->GetDeviceState(sizeof js, &js); + + if (FAILED(hr)) + continue; + +#if 1 + if (js.lX != 500 && js.lY != 500) { + POINT pt; + GetCursorPos(&pt); + + winfx = ((winfx + 0x8000) & 0xffff) + (pt.x << 16) - 0x8000; + winfy = ((winfy + 0x8000) & 0xffff) + (pt.y << 16) - 0x8000; + + winfx += js.lX * acscale; + winfy += js.lY * acscale; + + acscale += pow(hypot(js.lX, js.lY) / 1000.0, 2.0) * 1000.0 / 16.0; + + winx = (winfx + 0x8000) >> 16; + winy = (winfy + 0x8000) >> 16; + + if (absmode) { + winx = 1600 * (js.lX + 1000) / 2001; + winy = 1200 * (js.lY + 1000) / 2001; + } + + if (winfx < (0 << 16)) + winfx = 0 << 16; + if (winfx > (1600 << 16)) + winfx = 1600 << 16; + if (winfy < (0 << 16)) + winfy = 0 << 16; + if (winfy > (1200 << 16)) + winfy = 1200 << 16; + + // SetCursorPos(winx + 300*js.lX/1000, winy + + // 300*js.lY/1000); + + SetCursorPos(winx, winy); + } + + bool nowleft = + (js.rgbButtons[1] | js.rgbButtons[10] | js.rgbButtons[11]) >= 0x80 || + LOWORD(js.rgdwPOV[0]) != 0xffff; + bool nowright = js.rgbButtons[0] >= 0x80; + bool nowabs = js.rgbButtons[2] >= 0x80; + + if (fabs(js.lX) < 128 && fabs(js.lY) < 128) + acscale = 128; + + if (left && !nowleft) + mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, NULL); + if (!left && nowleft) + mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, NULL); + if (right && !nowright) + mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, NULL); + if (!right && nowright) + mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, NULL); + + if (!abs && nowabs) + absmode = !absmode; + + left = nowleft; + right = nowright; + abs = nowabs; +#else + bool nowleft = (js.rgbButtons[0] | js.rgbButtons[1] | js.rgbButtons[2] | + js.rgbButtons[3]) >= 0x80; + + if (nowleft && !left) { + keybd_event(VK_NEXT, 0, 0, 0); + } else if (!nowleft && left) { + keybd_event(VK_NEXT, 0, KEYEVENTF_KEYUP, 0); + } + left = nowleft; +#endif + } + } + shutdown(); + return 0; +} diff --git a/test/xitest.cpp b/test/xitest.cpp new file mode 100644 index 0000000..cd2a62c --- /dev/null +++ b/test/xitest.cpp @@ -0,0 +1,6 @@ +#include "xinput.h" + +int main(int argc, char const *argv[]) { + XInputEnable(true); + return 0; +} From d171785f681b6784dccdb30c3ae30054a4c648f8 Mon Sep 17 00:00:00 2001 From: Beren Minor Date: Sat, 1 Jul 2017 14:54:16 +0200 Subject: [PATCH 3/5] Use wine headers instead of re-declaring XInput structures and functions Some functions and macros are missing from the wine headers though, we can declare them. --- CMakeLists.txt | 3 ++ xinput.cpp | 7 ++-- xinput.h | 104 +++++++------------------------------------------ 3 files changed, 20 insertions(+), 94 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b7111a8..5e3a5b1 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,9 @@ if (NOT CMAKE_C_COMPILER MATCHES "i686-w64-mingw32-gcc" add_library(koku-xinput-wine SHARED main.h main.cpp xinput.h xinput.cpp device.h device.cpp) set_target_properties(koku-xinput-wine PROPERTIES CXX_STANDARD 11 PREFIX "") + target_include_directories(koku-xinput-wine PRIVATE + /usr/include/wine-development/windows + /usr/include/wine/windows) find_package(PkgConfig REQUIRED) pkg_check_modules(SDL2 REQUIRED sdl2) diff --git a/xinput.cpp b/xinput.cpp index 054c272..b0ba956 100755 --- a/xinput.cpp +++ b/xinput.cpp @@ -13,6 +13,8 @@ #include #include +#include + #define WINE_XINPUT_AXES 8 #define MAX_XINPUT_BUTTONS 14 using namespace std; @@ -28,7 +30,6 @@ struct Sgamepad_sdl static vector gamepads_sdl; static const unsigned int xbuttons[MAX_XINPUT_BUTTONS] = {XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB, XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y, XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_RIGHT, XINPUT_GAMEPAD_DPAD_UP}; static const SDL_GameControllerButton sdlbuttons[MAX_XINPUT_BUTTONS] = {SDL_CONTROLLER_BUTTON_START, SDL_CONTROLLER_BUTTON_BACK, SDL_CONTROLLER_BUTTON_LEFTSTICK, SDL_CONTROLLER_BUTTON_RIGHTSTICK, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, SDL_CONTROLLER_BUTTON_A, SDL_CONTROLLER_BUTTON_B, SDL_CONTROLLER_BUTTON_X, SDL_CONTROLLER_BUTTON_Y, SDL_CONTROLLER_BUTTON_DPAD_DOWN, SDL_CONTROLLER_BUTTON_DPAD_LEFT, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, SDL_CONTROLLER_BUTTON_DPAD_UP}; -GUID GUID_NULL = {0,0,0,{0,0,0,0,0,0,0,0}}; void GamepadInitSDL() { @@ -176,7 +177,7 @@ unsigned WINAPI XInputGetCapabilities(unsigned dwUserIndex, unsigned dwFlags, XI { return ERROR_DEVICE_NOT_CONNECTED; } - + if (pCapabilities) { pCapabilities->Type = XINPUT_DEVTYPE_GAMEPAD; @@ -249,7 +250,7 @@ unsigned WINAPI XInputGetState(unsigned dwUserIndex, XINPUT_STATE *pState) SDL_GameControllerUpdate(); //set data - if (pState) + if (pState) { SDL_GameController* controller = gamepads_sdl[dwUserIndex].controller; diff --git a/xinput.h b/xinput.h index 8d507c5..ee9e8b7 100755 --- a/xinput.h +++ b/xinput.h @@ -1,99 +1,21 @@ #ifndef KOKU_XINPUT_H #define KOKU_XINPUT_H -#define WINAPI __attribute__((__stdcall__)) -#define ERROR_SUCCESS 0x0 -#define ERROR_DEVICE_NOT_CONNECTED 0x48f -#define ERROR_EMPTY 0x10D2 -#define BATTERY_DEVTYPE_GAMEPAD 0x00 -#define BATTERY_DEVTYPE_HEADSET 0x01 -#define BATTERY_TYPE_DISCONNECTED 0x00 -#define BATTERY_TYPE_WIRED 0x01 -#define BATTERY_TYPE_ALKALINE 0x02 -#define BATTERY_TYPE_NIMH 0x03 -#define BATTERY_TYPE_UNKNOWN 0xFF -#define BATTERY_LEVEL_EMPTY 0x00 -#define BATTERY_LEVEL_LOW 0x01 -#define BATTERY_LEVEL_MEDIUM 0x02 -#define BATTERY_LEVEL_FULL 0x03 -#define XINPUT_CAPS_FFB_SUPPORTED 0x0001 -#define XINPUT_GAMEPAD_DPAD_UP 0x0001 -#define XINPUT_GAMEPAD_DPAD_DOWN 0x0002 -#define XINPUT_GAMEPAD_DPAD_LEFT 0x0004 -#define XINPUT_GAMEPAD_DPAD_RIGHT 0x0008 -#define XINPUT_GAMEPAD_START 0x0010 -#define XINPUT_GAMEPAD_BACK 0x0020 -#define XINPUT_GAMEPAD_LEFT_THUMB 0x0040 -#define XINPUT_GAMEPAD_RIGHT_THUMB 0x0080 -#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100 -#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200 -#define XINPUT_GAMEPAD_A 0x1000 -#define XINPUT_GAMEPAD_B 0x2000 -#define XINPUT_GAMEPAD_X 0x4000 -#define XINPUT_GAMEPAD_Y 0x8000 -#define XINPUT_DEVTYPE_GAMEPAD 0x01 -#define XINPUT_DEVSUBTYPE_GAMEPAD 0x01 - -struct XINPUT_GAMEPAD -{ - unsigned short wButtons; - unsigned char bLeftTrigger; - unsigned char bRightTrigger; - short sThumbLX; - short sThumbLY; - short sThumbRX; - short sThumbRY; -}; - -struct XINPUT_STATE -{ - unsigned dwPacketNumber; - XINPUT_GAMEPAD Gamepad; -}; -struct XINPUT_BATTERY_INFORMATION -{ - unsigned char BatteryType; - unsigned char BatteryLevel; -}; +#define _FORCENAMELESSUNION +#define CINTERFACE +#define INITGUID -struct XINPUT_VIBRATION -{ - unsigned short wLeftMotorSpeed; - unsigned short wRightMotorSpeed; -}; +#include -struct XINPUT_CAPABILITIES -{ - unsigned char Type; - unsigned char SubType; - unsigned short Flags; - XINPUT_GAMEPAD Gamepad; - XINPUT_VIBRATION Vibration; -}; - -struct XINPUT_KEYSTROKE -{ - unsigned short VirtualKey; - unsigned short Unicode; - unsigned short Flags; - unsigned char UserIndex; - unsigned char HidCode; -}; +#define XINPUT_CAPS_FFB_SUPPORTED 0x0001 -struct GUID -{ - unsigned Data1; - unsigned short Data2; - unsigned short Data3; - unsigned char Data4[8]; -}; +DWORD WINAPI XInputGetAudioDeviceIds +( + DWORD dwUserIndex, // Index of the gamer associated with the device + LPWSTR pRenderDeviceId, // Windows Core Audio device ID string for render (speakers) + UINT* pRenderCount, // Size of render device ID string buffer (in wide-chars) + LPWSTR pCaptureDeviceId, // Windows Core Audio device ID string for capture (microphone) + UINT* pCaptureCount // Size of capture device ID string buffer (in wide-chars) +); -void WINAPI XInputEnable(bool enable); -unsigned WINAPI XInputGetAudioDeviceIds(unsigned dwUserIndex, short* pRenderDeviceId, unsigned *pRenderCount, short* pCaptureDeviceId, unsigned *pCaptureCount); -unsigned WINAPI XInputGetBatteryInformation(unsigned dwUserIndex, char devType, XINPUT_BATTERY_INFORMATION *pBatteryInformation); -unsigned WINAPI XInputGetCapabilities(unsigned dwUserIndex, unsigned dwFlags, XINPUT_CAPABILITIES *pCapabilities); -unsigned WINAPI XInputGetDSoundAudioDeviceGuids(unsigned dwUserIndex, GUID* pDSoundRenderGuid, GUID* pDSoundCaptureGuid); -unsigned WINAPI XInputGetKeystroke(unsigned dwUserIndex, unsigned dwReserved, XINPUT_KEYSTROKE* pKeystroke); -unsigned WINAPI XInputGetState(unsigned dwUserIndex, XINPUT_STATE *pState); -unsigned WINAPI XInputSetState(unsigned dwUserIndex, XINPUT_VIBRATION *pVibration); #endif From fd4b714d562f31a32de16ca21e1eb4a73153f11e Mon Sep 17 00:00:00 2001 From: Beren Minor Date: Sat, 1 Jul 2017 14:22:22 +0200 Subject: [PATCH 4/5] Cleanup everything, verified on both 32bit and 64bit games: - Add 32bit/64bit jumper code - Use wine headers instead of re-declaring things - Cleaner hooking of the Device API using Wine vtable descriptions - Hook additional XInput DLL versions (9.1.0 and 1.4) - Add missing XInputGetStateEx hooking that some games use --- .clang-format | 3 + CMakeLists.txt | 6 +- device.cpp | 318 ++++++++++--------------- device.h | 35 --- jumper.h | 93 ++++++++ main.cpp | 88 ++----- main.h | 33 ++- xinput.cpp | 627 +++++++++++++++++++++++++++---------------------- xinput.h | 21 -- 9 files changed, 606 insertions(+), 618 deletions(-) create mode 100644 .clang-format delete mode 100644 device.h create mode 100644 jumper.h delete mode 100755 xinput.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..5bbf99b --- /dev/null +++ b/.clang-format @@ -0,0 +1,3 @@ +--- +DisableFormat: false +... diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e3a5b1..6e59ba1 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,12 +31,14 @@ if (NOT CMAKE_C_COMPILER MATCHES "i686-w64-mingw32-gcc" add_compile_options(-m32) endif() - add_library(koku-xinput-wine SHARED main.h main.cpp xinput.h xinput.cpp - device.h device.cpp) + add_library(koku-xinput-wine SHARED main.h main.cpp xinput.cpp + device.cpp jumper.h) set_target_properties(koku-xinput-wine PROPERTIES CXX_STANDARD 11 PREFIX "") target_include_directories(koku-xinput-wine PRIVATE /usr/include/wine-development/windows /usr/include/wine/windows) + target_compile_options(koku-xinput-wine PRIVATE -Wno-ignored-attributes + -Wno-subobject-linkage) find_package(PkgConfig REQUIRED) pkg_check_modules(SDL2 REQUIRED sdl2) diff --git a/device.cpp b/device.cpp index 267e75e..3cc5826 100644 --- a/device.cpp +++ b/device.cpp @@ -1,208 +1,148 @@ -#include "device.h" #include "main.h" -#include -#include -#include -#include -using namespace std; - -std::string check_bstrNamespace = "\\\\.\\root\\cimv2"; -std::string check_bstrClassName = "Win32_PNPEntity"; -std::string check_bstrDeviceID = "DeviceID"; - -const short wine_gamepad[] = {'V','I','D','_','3','E','D','9','&', - 'P','I','D','_','9','E','5','7','&', - 'I','G','_','0','0'}; //you can get VID/PID from wine-source - -//what this does ? well.. hard to explain, please don't mind as long as it works -long CoSetProxyBlanket_addr = 0; -char CoSetProxyBlanket_hook[sizeof(Sjmp)]; -long CreateInstanceEnum_addr = 0; -long CreateInstanceEnum_original; -long Next_addr = 0; -long Next_original; - -void DeviceInit(void* handle) -{ - if (debug) - { - clog << "koku-xinput-wine: search for `CoSetProxyBlanket`"; - } - //hook functions - long addr = long(dlsym(handle, "CoSetProxyBlanket")); - - if (addr != 0) - { - if (debug) - { - clog << ", found, redirect it"; - } - //backup data - CoSetProxyBlanket_addr = addr; - memcpy(CoSetProxyBlanket_hook, (void*)CoSetProxyBlanket_addr, sizeof(Sjmp)); - - long addr_start = (addr - PAGESIZE-1) & ~(PAGESIZE-1); - long addr_end = (addr + PAGESIZE-1) & ~(PAGESIZE-1); - mprotect((void*)addr_start, addr_end-addr_start , PROT_READ|PROT_WRITE|PROT_EXEC); - - new ((void*)addr) Sjmp((void*)CoSetProxyBlanket); - } - if (debug) - { - clog << endl; - } -} -void* WINAPI CoSetProxyBlanket(void* pProxy, unsigned dwAuthnSvc, unsigned dwAuthzSvc, void* pServerPrincName, unsigned dwAuthnLevel, unsigned dwImpLevel, void* pAuthInfo, unsigned dwCapabilities) -{ - if (debug) - { - clog << "koku-xinput-wine: CoSetProxyBlanket(...);" << endl; - } - - //disable hook - memcpy((void*)CoSetProxyBlanket_addr, CoSetProxyBlanket_hook, sizeof(Sjmp)); - //call original - void* result = ((decltype(&CoSetProxyBlanket))CoSetProxyBlanket_addr)(pProxy, dwAuthnSvc, dwAuthzSvc, pServerPrincName, dwAuthnLevel, dwImpLevel, pAuthInfo, dwCapabilities); - //enable hook - new ((void*)CoSetProxyBlanket_addr) Sjmp((void*)CoSetProxyBlanket); - //overwrite the function-table that CreateInstanceEnum goes to our function - long pProxy_func = *((long*)pProxy); - long pProxy_func_createinstanceenum = pProxy_func+0x48; - - long addr_start = (pProxy_func_createinstanceenum - PAGESIZE-1) & ~(PAGESIZE-1); - long addr_end = (pProxy_func_createinstanceenum + PAGESIZE-1) & ~(PAGESIZE-1); - mprotect((void*)addr_start, addr_end-addr_start , PROT_READ|PROT_WRITE|PROT_EXEC); - - if (*((void**)(pProxy_func_createinstanceenum)) != (void*)CreateInstanceEnum) - { - CreateInstanceEnum_original = *((long*)(pProxy_func_createinstanceenum)); - CreateInstanceEnum_addr = pProxy_func_createinstanceenum; - } - - *((void**)(pProxy_func_createinstanceenum)) = (void*)CreateInstanceEnum; - - return result; -} +#define _FORCENAMELESSUNION +#define CINTERFACE +#define INITGUID -void* WINAPI CreateInstanceEnum(void* pIWbemServices, short* bstrClassName, unsigned null1, void* null2, void* pEnumDevices) -{ - //check, uhm i have no idea how to work with unicode.. - string bstrClassName_s; - for(int i = 0; bstrClassName[i] != 0; ++i) - { - bstrClassName_s += bstrClassName[i]; - } +#include "objbase.h" +#include "wbemcli.h" - if (debug) - { - clog << "koku-xinput-wine: CreateInstanceEnum(..., \"" << bstrClassName_s << "\", ...);" << endl; - } +namespace koku { +unsigned short wine_gamepad[] = {'V', 'I', 'D', '_', '3', 'E', 'D', '9', + '&', 'P', 'I', 'D', '_', '9', 'E', '5', + '7', '&', 'I', 'G', '_', '0', '0'}; +unsigned short x360_gamepad[] = {'V', 'I', 'D', '_', '0', '4', '5', 'E', + '&', 'P', 'I', 'D', '_', '0', '2', '8', + 'E', '&', 'I', 'G', '_', '0', '0'}; - //call original - void* result = ((decltype(&CreateInstanceEnum))CreateInstanceEnum_original)(pIWbemServices, bstrClassName, null1, null2, pEnumDevices); +HRESULT STDMETHODCALLTYPE IWbemClassObject_Get(IWbemClassObject *This, + LPCWSTR wszName, LONG lFlags, + VARIANT *pVal, CIMTYPE *pType, + LONG *plFlavor) { + debug(""); - if (bstrClassName_s != check_bstrClassName) - { + auto wszNameLen = ((uint32_t *)wszName)[-1]; + if (std::memcmp(wszName, u"DeviceID", wszNameLen) == 0) { + debug("DeviceID"); - return result; - } + pVal->vt = VT_BSTR; + pVal->bstrVal = x360_gamepad; - //overwrite the function-table that Next goes to our function - long pEnumDevices_func = **((long**)pEnumDevices); - long pEnumDevices_func_next = pEnumDevices_func+0x10; + return 0; + } - long addr_start = (pEnumDevices_func_next - PAGESIZE-1) & ~(PAGESIZE-1); - long addr_end = (pEnumDevices_func_next + PAGESIZE-1) & ~(PAGESIZE-1); - mprotect((void*)addr_start, addr_end-addr_start , PROT_READ|PROT_WRITE|PROT_EXEC); + return 1; +} + +ULONG STDMETHODCALLTYPE IWbemClassObject_Release(IWbemClassObject *This) { + debug(""); + delete This->lpVtbl; + delete This; + return 0; +} - if (*((void**)(pEnumDevices_func_next)) != (void*)EnumDevices_Next) - { - Next_addr = pEnumDevices_func_next; - Next_original = *((long*)(pEnumDevices_func_next)); - } +koku::jumper::type> + IEnumWbemClassObject_Next_Jumper; +HRESULT STDMETHODCALLTYPE IEnumWbemClassObject_Next_Koku( + IEnumWbemClassObject *This, LONG lTimeout, ULONG uCount, + IWbemClassObject **apObjects, ULONG *puReturned) { + debug(""); + auto result = IEnumWbemClassObject_Next_Jumper(This, lTimeout, uCount, + apObjects, puReturned); - *((void**)(pEnumDevices_func_next)) = (void*)EnumDevices_Next; + if (*puReturned == 0) { + auto wbemClassObject = new IWbemClassObject{}; + wbemClassObject->lpVtbl = new IWbemClassObjectVtbl{}; + + wbemClassObject->lpVtbl->Get = &IWbemClassObject_Get; + wbemClassObject->lpVtbl->Release = &IWbemClassObject_Release; + + *puReturned = 1; + *apObjects = wbemClassObject; + } + + return result; +} - return result; +koku::jumper::type> + IWbemServices_CreateInstanceEnum_Jumper; +HRESULT STDMETHODCALLTYPE IWbemServices_CreateInstanceEnum_Koku( + IWbemServices *This, const BSTR strFilter, LONG lFlags, IWbemContext *pCtx, + IEnumWbemClassObject **ppEnum) { + debug(""); + auto result = IWbemServices_CreateInstanceEnum_Jumper(This, strFilter, lFlags, + pCtx, ppEnum); + + auto strFilterLen = ((uint32_t *)strFilter)[-1]; + if (std::memcmp(strFilter, u"Win32_PNPEntity", strFilterLen) == 0) { + auto enumWbemClassObject = *ppEnum; + if (enumWbemClassObject != nullptr && + IEnumWbemClassObject_Next_Jumper.src != + enumWbemClassObject->lpVtbl->Next) { + IEnumWbemClassObject_Next_Jumper = koku::make_jumper( + enumWbemClassObject->lpVtbl->Next, &IEnumWbemClassObject_Next_Koku); + debug("found IEnumWbemClassObject_Next at %p, redirecting it to %p", + enumWbemClassObject->lpVtbl->Next, &IEnumWbemClassObject_Next_Koku); + } + } + + return result; } -void* WINAPI EnumDevices_Next(void* pEnumDevices, unsigned a, unsigned b, void** pDevices, unsigned* uReturned) -{ - if (debug) - { - clog << "koku-xinput-wine: EnumDevices_Next(...);" << endl; - } - - //call original - void* result = ((decltype(&EnumDevices_Next))Next_original)(pEnumDevices, a, b, pDevices, uReturned); - - if (*uReturned == 0) - { - if (debug) - { - clog << "koku-xinput-wine: Return own custom-data;" << endl; - } - //restore original - *((void**)(Next_addr)) = (void*)Next_original; - - //end reach add our own stuff ;) - *uReturned = 1; - - //Very ugly stuff will happen now: - *pDevices = (void*)(new char[1024]); - *((void**)*pDevices) = (void*)(unsigned(*pDevices)+1); - - long pDevices_func = **((long**)pDevices); - long pDevices_func_get = pDevices_func+0x10; - - long addr_start = (pDevices_func_get - PAGESIZE-1) & ~(PAGESIZE-1); - long addr_end = (pDevices_func_get + PAGESIZE-1) & ~(PAGESIZE-1); - mprotect((void*)addr_start, addr_end-addr_start , PROT_READ|PROT_WRITE|PROT_EXEC); - - *((void**)(pDevices_func_get)) = (void*)Devices_Get; - pDevices_func_get = pDevices_func+0x08; - *((void**)(pDevices_func_get)) = (void*)Devices_Release; - } - return result; +koku::jumper::type> + IWbemLocator_ConnectServer_Jumper; +HRESULT STDMETHODCALLTYPE IWbemLocator_ConnectServer_Koku( + IWbemLocator *This, const BSTR strNetworkResource, const BSTR strUser, + const BSTR strPassword, const BSTR strLocale, LONG lSecurityFlags, + const BSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace) { + debug(""); + auto result = IWbemLocator_ConnectServer_Jumper( + This, strNetworkResource, strUser, strPassword, strLocale, lSecurityFlags, + strAuthority, pCtx, ppNamespace); + + auto wbemServices = *ppNamespace; + if (wbemServices != nullptr && IWbemServices_CreateInstanceEnum_Jumper.src != + wbemServices->lpVtbl->CreateInstanceEnum) { + IWbemServices_CreateInstanceEnum_Jumper = + koku::make_jumper(wbemServices->lpVtbl->CreateInstanceEnum, + &IWbemServices_CreateInstanceEnum_Koku); + debug("found IWbemServices_CreateInstanceEnum at %p, redirecting it to %p", + wbemServices->lpVtbl->CreateInstanceEnum, + &IWbemServices_CreateInstanceEnum_Koku); + } + + return result; } -bool WINAPI Devices_Get(void* pDevices, short* wszName, unsigned lFlags, VARIANT* pVal, void* o1, void* o2) -{ - //check, uhm i have no idea how to work with unicode.. - string wszName_s; - for(int i = 0; wszName[i] != 0; ++i) - { - wszName_s += wszName[i]; - } - - if (debug) - { - clog << "koku-xinput-wine: Devices_Get(..., \""<< wszName_s << "\");" << endl; - } - - if (check_bstrDeviceID != wszName_s) - { - //uhm nothing - return 1; //not ERROR_SUCCESS - } - - if (debug) - { - clog << "koku-xinput-wine: return wine-gamepad" << endl; - } - - pVal->vt = /*VT_BSTR*/8; - pVal->STR = wine_gamepad; - - return 0; +koku::jumper CoCreateInstance_Jumper; +HRESULT WINAPI CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, + DWORD dwClsContext, REFIID iid, LPVOID *ppv) { + debug(""); + auto result = + CoCreateInstance_Jumper(rclsid, pUnkOuter, dwClsContext, iid, ppv); + + if (std::memcmp(&iid, &IID_IWbemLocator, sizeof(iid)) == 0) { + auto wbemLocator = *(IWbemLocator **)ppv; + if (wbemLocator != nullptr && IWbemLocator_ConnectServer_Jumper.src != + wbemLocator->lpVtbl->ConnectServer) { + IWbemLocator_ConnectServer_Jumper = koku::make_jumper( + wbemLocator->lpVtbl->ConnectServer, &IWbemLocator_ConnectServer_Koku); + debug("found IWbemLocator_ConnectServer at %p, redirecting it to %p", + wbemLocator->lpVtbl->ConnectServer, + &IWbemLocator_ConnectServer_Koku); + } + } + + return result; } -void WINAPI Devices_Release(void* pDevices) -{ - if (debug) - { - clog << "koku-xinput-wine: Devices_Release(...);" << endl; - } - delete[] (char*)pDevices; +void DeviceInit(void *handle) { + if (auto address = + (decltype(&CoCreateInstance))dlsym(handle, "CoCreateInstance")) { + CoCreateInstance_Jumper = + koku::make_jumper(address, &koku::CoCreateInstance); + debug("found CoCreateInstance at %p, redirecting it to %p", address, + &koku::CoCreateInstance); + } } +} // namespace koku diff --git a/device.h b/device.h deleted file mode 100644 index 8c64e30..0000000 --- a/device.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - - Pretty much for - http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014(v=vs.85).aspx - - Some games check this way for XInput support - - The most simple way would be to fake the values wine returns.. - Sadly it's not that easy.. because wine doesn't return any devices.. - - Means we need todo some dark magic, this code will be ugly.. - */ - - #ifndef KOKU_DEVICE_H - #define KOKU_DEVICE_H - - #include "xinput.h" //for WINAPI macro - -struct VARIANT -{ - unsigned short vt; - short wReserved1; - short wReserved2; - short wReserved3; - const short *STR; -}; - -void DeviceInit(void* handle); -bool WINAPI Devices_Get(void* pDevices, short* wszName, unsigned lFlags, VARIANT* pVal, void* o1, void* o2); -void WINAPI Devices_Release(void* pDevices); -void* WINAPI EnumDevices_Next(void* pEnumDevices, unsigned a, unsigned b, void** pDevices, unsigned* uReturned); -void* WINAPI WbemServices_CreateInstanceEnum(void* pIWbemServices, short* bstrClassName, unsigned null1, void* null2, void* pEnumDevices); -void* WINAPI CreateInstanceEnum(void* pIWbemServices, short* bstrClassName, unsigned null1, void* null2, void* pEnumDevices); -void* WINAPI CoSetProxyBlanket(void* pProxy, unsigned dwAuthnSvc, unsigned dwAuthzSvc, void* pServerPrincName, unsigned dwAuthnLevel, unsigned dwImpLevel, void* pAuthInfo, unsigned dwCapabilities); - #endif diff --git a/jumper.h b/jumper.h new file mode 100644 index 0000000..bf8dba2 --- /dev/null +++ b/jumper.h @@ -0,0 +1,93 @@ +#ifndef KOKU_JUMPER_H +#define KOKU_JUMPER_H + +#include +#include +#include +#include +#include + +#include +#include + +namespace koku { +template struct jumper { + F *src = nullptr; + F *dst = nullptr; + +#if UINTPTR_MAX == UINT64_MAX + std::array header = {0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xe0}; +#elif UINTPTR_MAX == UINT32_MAX + std::array header = {0xe9, 0x00, 0x00, 0x00, 0x00}; +#else +#error "Unsupported architecture" +#endif + + jumper() = default; + jumper(jumper const &) = default; + jumper(jumper &&) = default; + jumper &operator=(jumper const &) = default; + jumper &operator=(jumper &&) = default; + + jumper(F *src, F *dst) : src(src), dst(dst) { + assert(src != nullptr); + assert(dst != nullptr); + +#if UINTPTR_MAX == UINT64_MAX + std::memcpy(&header[2], &dst, 8); +#elif UINTPTR_MAX == UINT32_MAX + auto distance = (uintptr_t)std::abs((intptr_t)src - (intptr_t)dst); + assert(distance <= INT32_MAX); + auto rel = (uintptr_t)dst - ((uintptr_t)src + header.size()); + std::memcpy(&header[1], &rel, 4); +#else +#error "Unsupported architecture" +#endif + + unprotect(); + install(); + } + + void unprotect() { + assert(src != nullptr); + auto pagesize = sysconf(_SC_PAGESIZE); + auto address = (void *)((uintptr_t)src & ~(pagesize - 1)); + auto result = + mprotect(address, header.size(), PROT_READ | PROT_WRITE | PROT_EXEC); + assert(result == 0); + } + + void install() { + assert(src != nullptr); + auto tmp = header; + std::memcpy(&header[0], (const void *)src, header.size()); + std::memcpy((void *)src, &tmp[0], tmp.size()); + } + + void uninstall() { + assert(src != nullptr); + auto tmp = header; + std::memcpy(&header[0], (const void *)src, header.size()); + std::memcpy((void *)src, &tmp[0], tmp.size()); + } + + struct scoped_uninstall { + jumper &jmp; + scoped_uninstall(jumper &jmp) : jmp(jmp) { jmp.uninstall(); } + ~scoped_uninstall() { jmp.install(); } + }; + + template + auto operator()(As &&... as) -> decltype(src(std::forward(as)...)) { + auto _ = scoped_uninstall(*this); + return src(std::forward(as)...); + } +}; + +template jumper make_jumper(F *src, F *dst) { + return jumper{src, dst}; +} +} // namespace koku + +#endif // KOKU_JUMPER_H diff --git a/main.cpp b/main.cpp index f5b66ae..3854830 100644 --- a/main.cpp +++ b/main.cpp @@ -1,80 +1,22 @@ -#include "xinput.h" -#include #include "main.h" -#include "device.h" -#include -#include -using namespace std; - -#ifdef __LP64__ -#warning "Add 64bit support !" -#endif - -bool debug = false; -extern "C" void *wine_dll_load( const char *filename, char *error, int errorsize, int *file_exists ) -{ - debug = (getenv("KOKU_XINPUT_DEBUG") != 0); - - /* - This is a wine intern function, - we get control of this function via LD_PRELOAD. - - We check the filenames and hook some functions ;) - */ +#include - if (debug) - { - clog << "koku-xinput-wine: wine_dll_load(\"" << filename << "\", ...);" << endl; - } +extern "C" void *wine_dll_load(const char *filename, char *error, int errorsize, + int *file_exists) { + auto result = ((decltype(&wine_dll_load))dlsym(RTLD_NEXT, "wine_dll_load"))( + filename, error, errorsize, file_exists); + debug("wine_dll_load(%s, ...)", filename); - //call original function: - void* result = ((decltype(&wine_dll_load))dlsym(RTLD_NEXT, "wine_dll_load"))(filename, error, errorsize, file_exists); + if (std::string{"xinput1_3.dll"} == filename || + std::string{"xinput9_1_0.dll"} == filename || + std::string{"xinput1_4.dll"} == filename) { + koku::XInputInit(result); + } - //check for dlls - if (string("xinput1_3.dll") == filename) - { - long addr = 0; - pair list[] = - { - {"XInputEnable" , (void*)&XInputEnable}, - {"XInputGetAudioDeviceIds" , (void*)&XInputGetAudioDeviceIds}, - {"XInputGetBatteryInformation" , (void*)&XInputGetBatteryInformation}, - {"XInputGetCapabilities" , (void*)&XInputGetCapabilities}, - {"XInputGetDSoundAudioDeviceGuids" , (void*)&XInputGetDSoundAudioDeviceGuids}, - {"XInputGetKeystroke" , (void*)&XInputGetKeystroke}, - {"XInputGetState" , (void*)&XInputGetState}, - {"XInputSetState" , (void*)&XInputSetState} - }; - //hook functions - for(int i = 0; i < 8; ++i) - { - addr = long(dlsym(result, list[i].first.c_str())); - if (debug) - { - clog << "koku-xinput-wine: search for `" << list[i].first << "`"; - } - if (addr != 0) - { - if (debug) - { - clog << ", found, redirect it"; - } - long addr_start = (addr - PAGESIZE-1) & ~(PAGESIZE-1); - long addr_end = (addr + PAGESIZE-1) & ~(PAGESIZE-1); - mprotect((void*)addr_start, addr_end-addr_start, PROT_READ|PROT_WRITE|PROT_EXEC); - new ((void*)addr) Sjmp(list[i].second); - } - if (debug) - { - clog << endl; - } - } - } - if (string("ole32.dll") == filename) - { - DeviceInit(result); - } + if (std::string{"ole32.dll"} == filename) { + koku::DeviceInit(result); + } - return result; + return result; } diff --git a/main.h b/main.h index 5c3bb15..b6f88aa 100644 --- a/main.h +++ b/main.h @@ -1,23 +1,18 @@ +#ifndef KOKU_MAIN_H +#define KOKU_MAIN_H + #include -#include -#include -#include -#ifndef PAGESIZE -#define PAGESIZE 4096 -#endif -struct __attribute__((packed)) Sjmp -{ - unsigned char op; - void* value; +#include "jumper.h" + +#define debug(message, ...) \ + if (getenv("KOKU_XINPUT_DEBUG") != nullptr) \ + std::printf("koku-xinput-wine: [%d] %s:%d %s " message "\n", getpid(), \ + __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); - Sjmp(void* value): - op(0xE9), value((void*)((long)value-(long)&op-5)) - { - /* - This JITs a X86 jmp instruction - */ - } -}; +namespace koku { +void XInputInit(void *handle); +void DeviceInit(void *handle); +}; // namespace koku -extern bool debug; +#endif // KOKU_MAIN_H diff --git a/xinput.cpp b/xinput.cpp index b0ba956..49d15f6 100755 --- a/xinput.cpp +++ b/xinput.cpp @@ -1,316 +1,385 @@ -#include "xinput.h" #include "main.h" +#include +#include +#include + #include #include -#include -#include -#include -#include -#include -#include -#include -#include - #include +#include -#define WINE_XINPUT_AXES 8 -#define MAX_XINPUT_BUTTONS 14 -using namespace std; - -bool active = true; -struct Sgamepad_sdl -{ - SDL_Joystick *joystick; - SDL_GameController* controller; - SDL_Haptic *haptic; - int haptic_effects[2]; -}; -static vector gamepads_sdl; -static const unsigned int xbuttons[MAX_XINPUT_BUTTONS] = {XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB, XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y, XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_RIGHT, XINPUT_GAMEPAD_DPAD_UP}; -static const SDL_GameControllerButton sdlbuttons[MAX_XINPUT_BUTTONS] = {SDL_CONTROLLER_BUTTON_START, SDL_CONTROLLER_BUTTON_BACK, SDL_CONTROLLER_BUTTON_LEFTSTICK, SDL_CONTROLLER_BUTTON_RIGHTSTICK, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, SDL_CONTROLLER_BUTTON_A, SDL_CONTROLLER_BUTTON_B, SDL_CONTROLLER_BUTTON_X, SDL_CONTROLLER_BUTTON_Y, SDL_CONTROLLER_BUTTON_DPAD_DOWN, SDL_CONTROLLER_BUTTON_DPAD_LEFT, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, SDL_CONTROLLER_BUTTON_DPAD_UP}; - -void GamepadInitSDL() -{ - static bool inited = false; - if (inited) - { - return; - } - - inited = true; - //init: - SDL_Init(SDL_INIT_JOYSTICK|SDL_INIT_HAPTIC|SDL_INIT_GAMECONTROLLER); - SDL_JoystickEventState(SDL_IGNORE); - SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt"); - for(int i = 0; i < SDL_NumJoysticks(); ++i) - { - SDL_Joystick* joy = SDL_JoystickOpen(i); - if (joy) - { - Sgamepad_sdl new_gamepad; - SDL_GameController* controller = SDL_GameControllerOpen(i); - if (controller) { - - new_gamepad.joystick = joy; - new_gamepad.controller = controller; - new_gamepad.haptic = 0; - //check for haptic - - new_gamepad.haptic = SDL_HapticOpenFromJoystick(new_gamepad.joystick); - if (new_gamepad.haptic != 0) - { - //start haptic effects - SDL_HapticEffect effect[2]; - memset(&effect, 0, sizeof(SDL_HapticEffect)*2); - - effect[0].type = SDL_HAPTIC_SINE; //constant somehow don't work, or I don't undestand what it does.. - effect[0].periodic.direction.type = SDL_HAPTIC_CARTESIAN; - effect[0].periodic.direction.dir[0] = 1; - effect[0].periodic.period = 0; - effect[0].periodic.magnitude = 0; - effect[0].periodic.length = SDL_HAPTIC_INFINITY; - - effect[1] = effect[0]; - effect[1].periodic.direction.dir[0] = -1; - effect[1].periodic.magnitude = 0; - - new_gamepad.haptic_effects[0] = SDL_HapticNewEffect(new_gamepad.haptic, &(effect[0])); - new_gamepad.haptic_effects[1] = SDL_HapticNewEffect(new_gamepad.haptic, &(effect[1])); - - SDL_HapticRunEffect(new_gamepad.haptic, new_gamepad.haptic_effects[0], 1); - SDL_HapticRunEffect(new_gamepad.haptic, new_gamepad.haptic_effects[1], 1); - } - gamepads_sdl.push_back(new_gamepad); - } +#define XINPUT_CAPS_FFB_SUPPORTED 0x0001 - } - } +DWORD WINAPI XInputGetAudioDeviceIds(DWORD dwUserIndex, LPWSTR pRenderDeviceId, + UINT *pRenderCount, + LPWSTR pCaptureDeviceId, + UINT *pCaptureCount); - XINPUT_VIBRATION welcome_vibration; - welcome_vibration.wLeftMotorSpeed = 65535; - welcome_vibration.wRightMotorSpeed = 0; - XInputSetState(0, &welcome_vibration); - SDL_Delay(250); - welcome_vibration.wLeftMotorSpeed = 0; - welcome_vibration.wRightMotorSpeed = 0; - XInputSetState(0, &welcome_vibration); - SDL_Delay(250); - welcome_vibration.wLeftMotorSpeed = 0; - welcome_vibration.wRightMotorSpeed = 65535; - XInputSetState(0, &welcome_vibration); - SDL_Delay(250); - welcome_vibration.wLeftMotorSpeed = 0; - welcome_vibration.wRightMotorSpeed = 0; - XInputSetState(0, &welcome_vibration); -} +#define WINE_XINPUT_AXES 8 +#define MAX_XINPUT_BUTTONS 14 -void WINAPI XInputEnable(bool enable) -{ - if (!enable) - { - XINPUT_VIBRATION stop_vibration; - stop_vibration.wLeftMotorSpeed = 0; - stop_vibration.wRightMotorSpeed = 0; - for(int i = 0; i < 4; ++i) - { - XInputSetState(i, &stop_vibration); +namespace koku { +struct SDLGamepad { + SDL_Joystick *joystick; + SDL_GameController *controller; + SDL_Haptic *haptic; + int haptic_effects[2]; +}; +static std::vector gamepads; +static const unsigned int xbuttons[MAX_XINPUT_BUTTONS] = { + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_BACK, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_THUMB, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_DPAD_DOWN, + XINPUT_GAMEPAD_DPAD_LEFT, + XINPUT_GAMEPAD_DPAD_RIGHT, + XINPUT_GAMEPAD_DPAD_UP}; +static const SDL_GameControllerButton sdlbuttons[MAX_XINPUT_BUTTONS] = { + SDL_CONTROLLER_BUTTON_START, + SDL_CONTROLLER_BUTTON_BACK, + SDL_CONTROLLER_BUTTON_LEFTSTICK, + SDL_CONTROLLER_BUTTON_RIGHTSTICK, + SDL_CONTROLLER_BUTTON_LEFTSHOULDER, + SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, + SDL_CONTROLLER_BUTTON_A, + SDL_CONTROLLER_BUTTON_B, + SDL_CONTROLLER_BUTTON_X, + SDL_CONTROLLER_BUTTON_Y, + SDL_CONTROLLER_BUTTON_DPAD_DOWN, + SDL_CONTROLLER_BUTTON_DPAD_LEFT, + SDL_CONTROLLER_BUTTON_DPAD_RIGHT, + SDL_CONTROLLER_BUTTON_DPAD_UP}; + +static bool enabled = true; + +DWORD WINAPI XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration); +void GamepadInitSDL() { + static bool inited = false; + if (inited) + return; + debug(""); + + inited = true; + + SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER); + SDL_JoystickEventState(SDL_IGNORE); + SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt"); + + for (int i = 0; i < SDL_NumJoysticks(); ++i) { + SDL_Joystick *joy = SDL_JoystickOpen(i); + if (joy) { + SDLGamepad gamepad; + SDL_GameController *controller = SDL_GameControllerOpen(i); + + if (controller) { + gamepad.joystick = joy; + gamepad.controller = controller; + gamepad.haptic = 0; + + gamepad.haptic = SDL_HapticOpenFromJoystick(gamepad.joystick); + if (gamepad.haptic != 0) { + SDL_HapticEffect effect[2]; + memset(&effect, 0, sizeof(SDL_HapticEffect) * 2); + + effect[0].type = SDL_HAPTIC_SINE; + effect[0].periodic.direction.type = SDL_HAPTIC_CARTESIAN; + effect[0].periodic.direction.dir[0] = 1; + effect[0].periodic.period = 0; + effect[0].periodic.magnitude = 0; + effect[0].periodic.length = SDL_HAPTIC_INFINITY; + + effect[1] = effect[0]; + effect[1].periodic.direction.dir[0] = -1; + effect[1].periodic.magnitude = 0; + + gamepad.haptic_effects[0] = + SDL_HapticNewEffect(gamepad.haptic, &(effect[0])); + gamepad.haptic_effects[1] = + SDL_HapticNewEffect(gamepad.haptic, &(effect[1])); + + SDL_HapticRunEffect(gamepad.haptic, gamepad.haptic_effects[0], 1); + SDL_HapticRunEffect(gamepad.haptic, gamepad.haptic_effects[1], 1); } - } - active = enable; -} -unsigned WINAPI XInputGetAudioDeviceIds(unsigned dwUserIndex, short* pRenderDeviceId, unsigned *pRenderCount, short* pCaptureDeviceId, unsigned *pCaptureCount) -{ - GamepadInitSDL(); - if (dwUserIndex >= gamepads_sdl.size()) - { - return ERROR_DEVICE_NOT_CONNECTED; + gamepads.push_back(gamepad); + } } + } + + XINPUT_VIBRATION welcome_vibration; + welcome_vibration.wLeftMotorSpeed = 65535; + welcome_vibration.wRightMotorSpeed = 0; + koku::XInputSetState(0, &welcome_vibration); + SDL_Delay(250); + welcome_vibration.wLeftMotorSpeed = 0; + welcome_vibration.wRightMotorSpeed = 0; + koku::XInputSetState(0, &welcome_vibration); + SDL_Delay(250); + welcome_vibration.wLeftMotorSpeed = 0; + welcome_vibration.wRightMotorSpeed = 65535; + koku::XInputSetState(0, &welcome_vibration); + SDL_Delay(250); + welcome_vibration.wLeftMotorSpeed = 0; + welcome_vibration.wRightMotorSpeed = 0; + koku::XInputSetState(0, &welcome_vibration); +} - /* - If there is no headset connected to the controller, - the function will also retrieve ERROR_SUCCESS with NULL as the values - for pRenderDeviceId and pCaptureDeviceId. - */ - if (pRenderCount) - { - *pRenderDeviceId = 0; - } - if (pCaptureDeviceId) - { - *pCaptureDeviceId = 0; - } - return ERROR_SUCCESS; +koku::jumper XInputEnableJumper; +void WINAPI XInputEnable(BOOL enable) { + debug(""); + if (!enable) { + XINPUT_VIBRATION vibration; + vibration.wLeftMotorSpeed = 0; + vibration.wRightMotorSpeed = 0; + for (int i = 0; i < 4; ++i) + koku::XInputSetState(i, &vibration); + } + + enabled = enable; } -unsigned WINAPI XInputGetBatteryInformation(unsigned dwUserIndex, char devType, XINPUT_BATTERY_INFORMATION *pBatteryInformation) -{ - GamepadInitSDL(); - if (dwUserIndex >= gamepads_sdl.size()) - { - return ERROR_DEVICE_NOT_CONNECTED; +koku::jumper XInputSetStateJumper; +DWORD WINAPI XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration) { + GamepadInitSDL(); + debug(""); + if (dwUserIndex >= gamepads.size()) + return ERROR_DEVICE_NOT_CONNECTED; + + if (enabled && pVibration) { + if (gamepads[dwUserIndex].haptic != 0) { + SDL_HapticEffect effect[2]; + memset(&effect, 0, sizeof(SDL_HapticEffect) * 2); + + effect[0].type = SDL_HAPTIC_SINE; + effect[0].periodic.direction.type = SDL_HAPTIC_CARTESIAN; + effect[0].periodic.direction.dir[0] = 1; + effect[0].periodic.period = 0; + effect[0].periodic.magnitude = pVibration->wLeftMotorSpeed >> 1; + effect[0].periodic.length = SDL_HAPTIC_INFINITY; + + effect[1] = effect[0]; + effect[1].periodic.direction.dir[0] = -1; + effect[1].periodic.magnitude = pVibration->wRightMotorSpeed >> 1; + + SDL_HapticUpdateEffect(gamepads[dwUserIndex].haptic, + gamepads[dwUserIndex].haptic_effects[0], + &(effect[0])); + SDL_HapticUpdateEffect(gamepads[dwUserIndex].haptic, + gamepads[dwUserIndex].haptic_effects[1], + &(effect[1])); } + } - if (pBatteryInformation) - { - if (devType == BATTERY_DEVTYPE_GAMEPAD) - { - //sorry no real battery check - pBatteryInformation->BatteryType = BATTERY_TYPE_WIRED; - pBatteryInformation->BatteryLevel = BATTERY_LEVEL_FULL; - } - else - { - pBatteryInformation->BatteryType = BATTERY_TYPE_DISCONNECTED; - pBatteryInformation->BatteryLevel = BATTERY_LEVEL_EMPTY; - } - } - return ERROR_SUCCESS; + return ERROR_SUCCESS; } -unsigned WINAPI XInputGetCapabilities(unsigned dwUserIndex, unsigned dwFlags, XINPUT_CAPABILITIES *pCapabilities) -{ - GamepadInitSDL(); - if (dwUserIndex >= gamepads_sdl.size()) - { - return ERROR_DEVICE_NOT_CONNECTED; +koku::jumper XInputGetStateJumper; +DWORD WINAPI XInputGetState(DWORD dwUserIndex, XINPUT_STATE *pState) { + GamepadInitSDL(); + debug(""); + if (dwUserIndex >= gamepads.size()) + return ERROR_DEVICE_NOT_CONNECTED; + + SDL_JoystickUpdate(); + SDL_GameControllerUpdate(); + + if (pState) { + SDL_GameController *controller = gamepads[dwUserIndex].controller; + + for (int j = 0; j < MAX_XINPUT_BUTTONS; j++) { + Uint8 result = SDL_GameControllerGetButton(controller, sdlbuttons[j]); + if (result) { + pState->Gamepad.wButtons |= xbuttons[j]; + } else { + pState->Gamepad.wButtons &= ~(xbuttons[j]); + } } - if (pCapabilities) - { - pCapabilities->Type = XINPUT_DEVTYPE_GAMEPAD; - pCapabilities->SubType = XINPUT_DEVSUBTYPE_GAMEPAD; - pCapabilities->Flags = (gamepads_sdl[dwUserIndex].haptic != 0)?XINPUT_CAPS_FFB_SUPPORTED:0; - pCapabilities->Gamepad.wButtons = 0xFFFF; - pCapabilities->Gamepad.bLeftTrigger = 255; - pCapabilities->Gamepad.bRightTrigger = 255; - pCapabilities->Gamepad.sThumbLX = 32767; - pCapabilities->Gamepad.sThumbLY = 32767; - pCapabilities->Gamepad.sThumbRX = 32767; - pCapabilities->Gamepad.sThumbRY = 32767; - if (gamepads_sdl[dwUserIndex].haptic != 0) - { - pCapabilities->Vibration.wLeftMotorSpeed = 65535; - pCapabilities->Vibration.wRightMotorSpeed = 65535; - } - else - { - pCapabilities->Vibration.wLeftMotorSpeed = 0; - pCapabilities->Vibration.wRightMotorSpeed = 0; - } - } - return ERROR_SUCCESS; + short ly = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); + short ry = + SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); + + pState->Gamepad.sThumbLX = + SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); + pState->Gamepad.sThumbLY = (ly == 0 ? 0 : -ly - 1); + pState->Gamepad.sThumbRX = + SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); + pState->Gamepad.sThumbRY = ry == 0 ? 0 : -ry - 1; + pState->Gamepad.bLeftTrigger = + SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT); + pState->Gamepad.bRightTrigger = + SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT); + + static int dwPacketNumber = 0; + pState->dwPacketNumber = ++dwPacketNumber; + } + + return ERROR_SUCCESS; } -unsigned WINAPI XInputGetDSoundAudioDeviceGuids(unsigned dwUserIndex, GUID* pDSoundRenderGuid, GUID* pDSoundCaptureGuid) -{ - GamepadInitSDL(); - if (dwUserIndex >= gamepads_sdl.size()) - { - return ERROR_DEVICE_NOT_CONNECTED; - } +koku::jumper XInputGetKeystrokeJumper; +DWORD WINAPI XInputGetKeystroke(DWORD dwUserIndex, DWORD dwReserved, + PXINPUT_KEYSTROKE pKeystroke) { + GamepadInitSDL(); + debug(""); + if (dwUserIndex >= gamepads.size()) + return ERROR_DEVICE_NOT_CONNECTED; - /* - If there is no headset connected to the controller, - the function also retrieves ERROR_SUCCESS with GUID_NULL as the values - for pDSoundRenderGuid and pDSoundCaptureGuid. - */ - if (pDSoundRenderGuid) { - *pDSoundRenderGuid = GUID_NULL; - } - if (pDSoundCaptureGuid) { - *pDSoundCaptureGuid = GUID_NULL; - } - return ERROR_SUCCESS; + return ERROR_EMPTY; } -unsigned WINAPI XInputGetKeystroke(unsigned dwUserIndex, unsigned dwReserved, XINPUT_KEYSTROKE* pKeystroke) -{ - GamepadInitSDL(); - if (dwUserIndex >= gamepads_sdl.size()) - { - return ERROR_DEVICE_NOT_CONNECTED; +koku::jumper XInputGetCapabilitiesJumper; +DWORD WINAPI XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, + XINPUT_CAPABILITIES *pCapabilities) { + GamepadInitSDL(); + debug(""); + if (dwUserIndex >= gamepads.size()) + return ERROR_DEVICE_NOT_CONNECTED; + + if (pCapabilities) { + pCapabilities->Type = XINPUT_DEVTYPE_GAMEPAD; + pCapabilities->SubType = XINPUT_DEVSUBTYPE_GAMEPAD; + pCapabilities->Flags = (gamepads[dwUserIndex].haptic != 0) + ? /* XINPUT_CAPS_FFB_SUPPORTED */ 0x0001 + : 0; + pCapabilities->Gamepad.wButtons = 0xFFFF; + pCapabilities->Gamepad.bLeftTrigger = 255; + pCapabilities->Gamepad.bRightTrigger = 255; + pCapabilities->Gamepad.sThumbLX = 32767; + pCapabilities->Gamepad.sThumbLY = 32767; + pCapabilities->Gamepad.sThumbRX = 32767; + pCapabilities->Gamepad.sThumbRY = 32767; + if (gamepads[dwUserIndex].haptic != 0) { + pCapabilities->Vibration.wLeftMotorSpeed = 65535; + pCapabilities->Vibration.wRightMotorSpeed = 65535; + } else { + pCapabilities->Vibration.wLeftMotorSpeed = 0; + pCapabilities->Vibration.wRightMotorSpeed = 0; } + } + return ERROR_SUCCESS; +} - //If no new keys have been pressed, the return value is ERROR_EMPTY. - return ERROR_EMPTY; +koku::jumper + XInputGetDSoundAudioDeviceGuidsJumper; +DWORD WINAPI XInputGetDSoundAudioDeviceGuids(DWORD dwUserIndex, + GUID *pDSoundRenderGuid, + GUID *pDSoundCaptureGuid) { + GamepadInitSDL(); + debug(""); + if (dwUserIndex >= gamepads.size()) + return ERROR_DEVICE_NOT_CONNECTED; + + /* + If there is no headset connected to the controller, + the function also retrieves ERROR_SUCCESS with GUID_NULL as the values + for pDSoundRenderGuid and pDSoundCaptureGuid. + */ + if (pDSoundRenderGuid) + std::memset(pDSoundRenderGuid, 0, sizeof(GUID)); + + if (pDSoundCaptureGuid) + std::memset(pDSoundCaptureGuid, 0, sizeof(GUID)); + + return ERROR_SUCCESS; } -unsigned WINAPI XInputGetState(unsigned dwUserIndex, XINPUT_STATE *pState) -{ - GamepadInitSDL(); - if (dwUserIndex >= gamepads_sdl.size()) - { - return ERROR_DEVICE_NOT_CONNECTED; +koku::jumper + XInputGetBatteryInformationJumper; +DWORD WINAPI +XInputGetBatteryInformation(DWORD dwUserIndex, BYTE devType, + XINPUT_BATTERY_INFORMATION *pBatteryInformation) { + GamepadInitSDL(); + debug(""); + if (dwUserIndex >= gamepads.size()) + return ERROR_DEVICE_NOT_CONNECTED; + + if (pBatteryInformation) { + if (devType == BATTERY_DEVTYPE_GAMEPAD) { + pBatteryInformation->BatteryType = BATTERY_TYPE_WIRED; + pBatteryInformation->BatteryLevel = BATTERY_LEVEL_FULL; + } else { + pBatteryInformation->BatteryType = BATTERY_TYPE_DISCONNECTED; + pBatteryInformation->BatteryLevel = BATTERY_LEVEL_EMPTY; } + } - SDL_JoystickUpdate(); - SDL_GameControllerUpdate(); - - //set data - if (pState) - { - SDL_GameController* controller = gamepads_sdl[dwUserIndex].controller; - - for (int j = 0; j < MAX_XINPUT_BUTTONS; j++) { - - Uint8 result = SDL_GameControllerGetButton(controller, sdlbuttons[j]); - //printf("result for %d: %d\n", j, result); - if (result) { - pState->Gamepad.wButtons |= xbuttons[j]; - } - else { - pState->Gamepad.wButtons &= ~(xbuttons[j]); - } - } - - short ly = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); - short ry = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); - - pState->Gamepad.sThumbLX = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); - pState->Gamepad.sThumbLY = (ly == 0 ? 0 : -ly -1); - pState->Gamepad.sThumbRX = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); - pState->Gamepad.sThumbRY = ry == 0 ? 0 : -ry -1; - pState->Gamepad.bLeftTrigger = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT); - pState->Gamepad.bRightTrigger = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT); - - static int dwPacketNumber = 0; - pState->dwPacketNumber = ++dwPacketNumber; - } - return ERROR_SUCCESS; + return ERROR_SUCCESS; } -unsigned WINAPI XInputSetState(unsigned dwUserIndex, XINPUT_VIBRATION *pVibration) -{ - GamepadInitSDL(); - if (dwUserIndex >= gamepads_sdl.size()) - { - return ERROR_DEVICE_NOT_CONNECTED; - } +koku::jumper XInputGetStateExJumper; +DWORD WINAPI XInputGetStateEx(DWORD dwUserIndex, XINPUT_STATE_EX *pState) { + return koku::XInputGetState(dwUserIndex, (XINPUT_STATE *)pState); +} - if (active && pVibration) - { - if (gamepads_sdl[dwUserIndex].haptic != 0) - { - SDL_HapticEffect effect[2]; - memset(&effect, 0, sizeof(SDL_HapticEffect)*2); - - effect[0].type = SDL_HAPTIC_SINE; - effect[0].periodic.direction.type = SDL_HAPTIC_CARTESIAN; - effect[0].periodic.direction.dir[0] = 1; - effect[0].periodic.period = 0; - effect[0].periodic.magnitude = pVibration->wLeftMotorSpeed>>1; - effect[0].periodic.length = SDL_HAPTIC_INFINITY; - - effect[1] = effect[0]; - effect[1].periodic.direction.dir[0] = -1; - effect[1].periodic.magnitude = pVibration->wRightMotorSpeed>>1; - - SDL_HapticUpdateEffect(gamepads_sdl[dwUserIndex].haptic, gamepads_sdl[dwUserIndex].haptic_effects[0], &(effect[0])); - SDL_HapticUpdateEffect(gamepads_sdl[dwUserIndex].haptic, gamepads_sdl[dwUserIndex].haptic_effects[1], &(effect[1])); - } - } - return ERROR_SUCCESS; +void XInputInit(void *handle) { + if (auto address = (decltype(&XInputEnable))dlsym(handle, "XInputEnable")) { + debug("found XInputEnable at %p, redirecting it to %p", address, + &koku::XInputEnable); + XInputEnableJumper = koku::make_jumper(address, &koku::XInputEnable); + } + + if (auto address = + (decltype(&XInputSetState))dlsym(handle, "XInputSetState")) { + debug("found XInputSetState at %p, redirecting it to %p", address, + &koku::XInputSetState); + XInputSetStateJumper = koku::make_jumper(address, koku::XInputSetState); + } + + if (auto address = + (decltype(&XInputGetState))dlsym(handle, "XInputGetState")) { + debug("found XInputGetState at %p, redirecting it to %p", address, + &koku::XInputGetState); + XInputGetStateJumper = koku::make_jumper(address, koku::XInputGetState); + } + + if (auto address = + (decltype(&XInputGetKeystroke))dlsym(handle, "XInputGetKeystroke")) { + debug("found XInputGetKeystroke at %p, redirecting it to %p", address, + &koku::XInputGetKeystroke); + XInputGetKeystrokeJumper = + koku::make_jumper(address, koku::XInputGetKeystroke); + } + + if (auto address = (decltype(&XInputGetCapabilities))dlsym( + handle, "XInputGetCapabilities")) { + debug("found XInputGetCapabilities at %p, redirecting it to %p", address, + &koku::XInputGetCapabilities); + XInputGetCapabilitiesJumper = + koku::make_jumper(address, koku::XInputGetCapabilities); + } + + if (auto address = (decltype(&XInputGetDSoundAudioDeviceGuids))dlsym( + handle, "XInputGetDSoundAudioDeviceGuids")) { + debug("found XInputGetDSoundAudioDeviceGuids at %p, redirecting it to %p", + address, &koku::XInputGetDSoundAudioDeviceGuids); + XInputGetDSoundAudioDeviceGuidsJumper = + koku::make_jumper(address, koku::XInputGetDSoundAudioDeviceGuids); + } + + if (auto address = (decltype(&XInputGetBatteryInformation))dlsym( + handle, "XInputGetBatteryInformation")) { + debug("found XInputGetBatteryInformation %p, redirecting it to %p", address, + &koku::XInputGetBatteryInformation); + XInputGetBatteryInformationJumper = + koku::make_jumper(address, koku::XInputGetBatteryInformation); + } + + if (auto address = + (decltype(&XInputGetStateEx))dlsym(handle, "XInputGetStateEx")) { + debug("found XInputGetStateEx at %p, redirecting it to %p", address, + &koku::XInputGetStateEx); + XInputGetStateExJumper = + koku::make_jumper(address, &koku::XInputGetStateEx); + } } +} // namespace koku diff --git a/xinput.h b/xinput.h deleted file mode 100755 index ee9e8b7..0000000 --- a/xinput.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef KOKU_XINPUT_H -#define KOKU_XINPUT_H - -#define _FORCENAMELESSUNION -#define CINTERFACE -#define INITGUID - -#include - -#define XINPUT_CAPS_FFB_SUPPORTED 0x0001 - -DWORD WINAPI XInputGetAudioDeviceIds -( - DWORD dwUserIndex, // Index of the gamer associated with the device - LPWSTR pRenderDeviceId, // Windows Core Audio device ID string for render (speakers) - UINT* pRenderCount, // Size of render device ID string buffer (in wide-chars) - LPWSTR pCaptureDeviceId, // Windows Core Audio device ID string for capture (microphone) - UINT* pCaptureCount // Size of capture device ID string buffer (in wide-chars) -); - -#endif From 053b570a9526f722fad9f97510fc0f357ac85562 Mon Sep 17 00:00:00 2001 From: Beren Minor Date: Fri, 18 Aug 2017 09:54:03 +0200 Subject: [PATCH 5/5] Add wine-staging system include directories to the include path --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e59ba1..5f5ba4d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ if (NOT CMAKE_C_COMPILER MATCHES "i686-w64-mingw32-gcc" device.cpp jumper.h) set_target_properties(koku-xinput-wine PROPERTIES CXX_STANDARD 11 PREFIX "") target_include_directories(koku-xinput-wine PRIVATE + /opt/wine-staging/include/wine/windows /usr/include/wine-development/windows /usr/include/wine/windows) target_compile_options(koku-xinput-wine PRIVATE -Wno-ignored-attributes