It doesn't seem very useful to keep things separate, the files are pretty much dependent from each other anyway and it's only 1k LoC.
Signed-off-by: RĂ©mi Bernon rbernon@codeweavers.com ---
I'm going for more refactoring instead, I understand the intent with the hid separation but I don't think it's doing anything useful currently.
I doubt we'll ever need something else than an HID backend for this, but even if we do, it's most likely going to be device based too, and the only difference will be how we'll read the state from the device file, which is overall not going to be very different from HID.
dlls/xinput1_1/Makefile.in | 3 +- dlls/xinput1_2/Makefile.in | 3 +- dlls/xinput1_3/Makefile.in | 3 +- dlls/xinput1_3/hid.c | 510 ----------------- dlls/xinput1_3/main.c | 935 ++++++++++++++++++++++++++++++++ dlls/xinput1_3/xinput_main.c | 470 ---------------- dlls/xinput1_3/xinput_private.h | 37 -- dlls/xinput1_4/Makefile.in | 3 +- dlls/xinput9_1_0/Makefile.in | 3 +- 9 files changed, 940 insertions(+), 1027 deletions(-) delete mode 100644 dlls/xinput1_3/hid.c create mode 100644 dlls/xinput1_3/main.c delete mode 100644 dlls/xinput1_3/xinput_main.c delete mode 100644 dlls/xinput1_3/xinput_private.h
diff --git a/dlls/xinput1_1/Makefile.in b/dlls/xinput1_1/Makefile.in index a2b17a248cd..25572e18b21 100644 --- a/dlls/xinput1_1/Makefile.in +++ b/dlls/xinput1_1/Makefile.in @@ -5,7 +5,6 @@ DELAYIMPORTS = hid setupapi EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \ - hid.c \ - xinput_main.c + main.c
RC_SRCS = version.rc diff --git a/dlls/xinput1_2/Makefile.in b/dlls/xinput1_2/Makefile.in index fd38c6f6cc5..3358c2cd3c8 100644 --- a/dlls/xinput1_2/Makefile.in +++ b/dlls/xinput1_2/Makefile.in @@ -5,7 +5,6 @@ DELAYIMPORTS = hid setupapi EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \ - hid.c \ - xinput_main.c + main.c
RC_SRCS = version.rc diff --git a/dlls/xinput1_3/Makefile.in b/dlls/xinput1_3/Makefile.in index 3b4968fb6d8..93281d4e853 100644 --- a/dlls/xinput1_3/Makefile.in +++ b/dlls/xinput1_3/Makefile.in @@ -5,7 +5,6 @@ DELAYIMPORTS = hid setupapi EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \ - hid.c \ - xinput_main.c + main.c
RC_SRCS = version.rc diff --git a/dlls/xinput1_3/hid.c b/dlls/xinput1_3/hid.c deleted file mode 100644 index 00907b54bed..00000000000 --- a/dlls/xinput1_3/hid.c +++ /dev/null @@ -1,510 +0,0 @@ -/* - * The Wine project - Xinput Joystick HID interface - * Copyright 2018 Aric Stewart - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - - -#include <assert.h> -#include <stdarg.h> -#include <string.h> -#include <stdlib.h> - -#include "wine/debug.h" -#include "windef.h" -#include "winbase.h" -#include "winuser.h" -#include "winerror.h" -#include "winreg.h" -#include "wingdi.h" -#include "winnls.h" -#include "winternl.h" - -#include "setupapi.h" -#include "devpkey.h" -#include "hidusage.h" -#include "ddk/hidsdi.h" -#include "initguid.h" -#include "devguid.h" - -#include "xinput.h" -#include "xinput_private.h" - - -WINE_DEFAULT_DEBUG_CHANNEL(xinput); - -#define XINPUT_GAMEPAD_GUIDE 0x0400 - -struct axis_info -{ - LONG min; - LONG range; - USHORT bits; -}; - -struct hid_platform_private { - PHIDP_PREPARSED_DATA ppd; - HIDP_CAPS caps; - - HANDLE device; - WCHAR *device_path; - BOOL enabled; - - char *input_report_buf[2]; - char *output_report_buf; - - struct axis_info lx, ly, ltrigger, rx, ry, rtrigger; -}; - -static DWORD last_check = 0; - -static BOOL find_opened_device(SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail, int *free_slot) -{ - struct hid_platform_private *private; - int i; - - *free_slot = XUSER_MAX_COUNT; - for (i = XUSER_MAX_COUNT; i > 0; i--) - { - if (!(private = controllers[i - 1].platform_private)) *free_slot = i - 1; - else if (!wcscmp(detail->DevicePath, private->device_path)) return TRUE; - } - return FALSE; -} - -static void MarkUsage(struct hid_platform_private *private, WORD usage, LONG min, LONG max, USHORT bits) -{ - struct axis_info info = {min, max-min, bits}; - - switch (usage) - { - case HID_USAGE_GENERIC_X: private->lx = info; break; - case HID_USAGE_GENERIC_Y: private->ly = info; break; - case HID_USAGE_GENERIC_Z: private->ltrigger = info; break; - case HID_USAGE_GENERIC_RX: private->rx = info; break; - case HID_USAGE_GENERIC_RY: private->ry = info; break; - case HID_USAGE_GENERIC_RZ: private->rtrigger = info; break; - } -} - -static BOOL VerifyGamepad(PHIDP_PREPARSED_DATA ppd, XINPUT_CAPABILITIES *xinput_caps, struct hid_platform_private *private) -{ - HIDP_BUTTON_CAPS *button_caps; - HIDP_VALUE_CAPS *value_caps; - NTSTATUS status; - - int i; - int button_count = 0; - - /* Count buttons */ - memset(xinput_caps, 0, sizeof(XINPUT_CAPABILITIES)); - - if (!(button_caps = malloc(sizeof(*button_caps) * private->caps.NumberInputButtonCaps))) return FALSE; - status = HidP_GetButtonCaps(HidP_Input, button_caps, &private->caps.NumberInputButtonCaps, ppd); - if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetButtonCaps returned %#x\n", status); - else for (i = 0; i < private->caps.NumberInputButtonCaps; i++) - { - if (button_caps[i].UsagePage != HID_USAGE_PAGE_BUTTON) - continue; - if (button_caps[i].IsRange) - button_count = max(button_count, button_caps[i].Range.UsageMax); - else - button_count = max(button_count, button_caps[i].NotRange.Usage); - } - free(button_caps); - if (button_count < 11) - WARN("Too few buttons, continuing anyway\n"); - xinput_caps->Gamepad.wButtons = 0xffff; - - if (!(value_caps = malloc(sizeof(*value_caps) * private->caps.NumberInputValueCaps))) return FALSE; - status = HidP_GetValueCaps(HidP_Input, value_caps, &private->caps.NumberInputValueCaps, ppd); - if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetValueCaps returned %#x\n", status); - else for (i = 0; i < private->caps.NumberInputValueCaps; i++) - { - if (value_caps[i].UsagePage != HID_USAGE_PAGE_GENERIC) - continue; - if (value_caps[i].IsRange) - { - int u; - for (u = value_caps[i].Range.UsageMin; u <=value_caps[i].Range.UsageMax; u++) - MarkUsage(private, u, value_caps[i].LogicalMin, value_caps[i].LogicalMax, value_caps[i].BitSize); - } - else - MarkUsage(private, value_caps[i].NotRange.Usage, value_caps[i].LogicalMin, value_caps[i].LogicalMax, value_caps[i].BitSize); - } - free(value_caps); - - if (private->ltrigger.bits) - xinput_caps->Gamepad.bLeftTrigger = (1u << (sizeof(xinput_caps->Gamepad.bLeftTrigger) + 1)) - 1; - else - WARN("Missing axis LeftTrigger\n"); - if (private->rtrigger.bits) - xinput_caps->Gamepad.bRightTrigger = (1u << (sizeof(xinput_caps->Gamepad.bRightTrigger) + 1)) - 1; - else - WARN("Missing axis RightTrigger\n"); - if (private->lx.bits) - xinput_caps->Gamepad.sThumbLX = (1u << (sizeof(xinput_caps->Gamepad.sThumbLX) + 1)) - 1; - else - WARN("Missing axis ThumbLX\n"); - if (private->ly.bits) - xinput_caps->Gamepad.sThumbLY = (1u << (sizeof(xinput_caps->Gamepad.sThumbLY) + 1)) - 1; - else - WARN("Missing axis ThumbLY\n"); - if (private->rx.bits) - xinput_caps->Gamepad.sThumbRX = (1u << (sizeof(xinput_caps->Gamepad.sThumbRX) + 1)) - 1; - else - WARN("Missing axis ThumbRX\n"); - if (private->ry.bits) - xinput_caps->Gamepad.sThumbRY = (1u << (sizeof(xinput_caps->Gamepad.sThumbRY) + 1)) - 1; - else - WARN("Missing axis ThumbRY\n"); - - xinput_caps->Type = XINPUT_DEVTYPE_GAMEPAD; - xinput_caps->SubType = XINPUT_DEVSUBTYPE_GAMEPAD; - - if (private->caps.NumberOutputValueCaps > 0) - { - xinput_caps->Flags |= XINPUT_CAPS_FFB_SUPPORTED; - xinput_caps->Vibration.wLeftMotorSpeed = 255; - xinput_caps->Vibration.wRightMotorSpeed = 255; - } - - return TRUE; -} - -static BOOL init_controller(xinput_controller *controller, PHIDP_PREPARSED_DATA ppd, HIDP_CAPS *caps, HANDLE device, WCHAR *device_path) -{ - size_t size; - struct hid_platform_private *private; - - if (!(private = calloc(1, sizeof(struct hid_platform_private)))) return FALSE; - private->caps = *caps; - if (!VerifyGamepad(ppd, &controller->caps, private)) goto failed; - - TRACE("Found gamepad %s\n", debugstr_w(device_path)); - - private->ppd = ppd; - private->device = device; - if (!(private->input_report_buf[0] = calloc(1, private->caps.InputReportByteLength))) goto failed; - if (!(private->input_report_buf[1] = calloc(1, private->caps.InputReportByteLength))) goto failed; - if (!(private->output_report_buf = calloc(1, private->caps.OutputReportByteLength))) goto failed; - size = (lstrlenW(device_path) + 1) * sizeof(WCHAR); - if (!(private->device_path = malloc(size))) goto failed; - memcpy(private->device_path, device_path, size); - private->enabled = TRUE; - - memset(&controller->state, 0, sizeof(controller->state)); - memset(&controller->vibration, 0, sizeof(controller->vibration)); - - controller->platform_private = private; - return TRUE; - -failed: - free(private->device_path); - free(private->input_report_buf[0]); - free(private->input_report_buf[1]); - free(private->output_report_buf); - free(private); - return FALSE; -} - -void HID_find_gamepads(void) -{ - char buffer[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + MAX_PATH * sizeof(WCHAR)]; - SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail = (SP_DEVICE_INTERFACE_DETAIL_DATA_W *)buffer; - HDEVINFO device_info_set; - GUID hid_guid; - SP_DEVICE_INTERFACE_DATA interface_data; - PHIDP_PREPARSED_DATA ppd; - HANDLE device; - HIDP_CAPS caps; - NTSTATUS status; - DWORD idx; - int i; - - idx = GetTickCount(); - if ((idx - last_check) < 2000) - return; - - EnterCriticalSection(&xinput_crit); - - if ((idx - last_check) < 2000) - { - LeaveCriticalSection(&xinput_crit); - return; - } - last_check = idx; - - HidD_GetHidGuid(&hid_guid); - - device_info_set = SetupDiGetClassDevsW(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); - detail->cbSize = sizeof(*detail); - - ZeroMemory(&interface_data, sizeof(interface_data)); - interface_data.cbSize = sizeof(interface_data); - - idx = 0; - while (SetupDiEnumDeviceInterfaces(device_info_set, NULL, &hid_guid, idx++, - &interface_data)) - { - if (!SetupDiGetDeviceInterfaceDetailW(device_info_set, &interface_data, detail, sizeof(buffer), NULL, NULL)) - continue; - - if (!wcsstr(detail->DevicePath, L"IG_")) - continue; - - if (find_opened_device(detail, &i)) continue; /* already opened */ - if (i == XUSER_MAX_COUNT) break; /* no more slots */ - - device = CreateFileW(detail->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 ); - if (device == INVALID_HANDLE_VALUE) - continue; - - ppd = NULL; - if (!HidD_GetPreparsedData(device, &ppd)) - WARN("ignoring HID device, HidD_GetPreparsedData failed with error %u\n", GetLastError()); - else if ((status = HidP_GetCaps(ppd, &caps)) != HIDP_STATUS_SUCCESS) - WARN("ignoring HID device, HidP_GetCaps returned %#x\n", status); - else if (caps.UsagePage != HID_USAGE_PAGE_GENERIC) - WARN("ignoring HID device, unsupported usage page %04x\n", caps.UsagePage); - else if (caps.Usage != HID_USAGE_GENERIC_GAMEPAD && caps.Usage != HID_USAGE_GENERIC_JOYSTICK && caps.Usage != HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER) - WARN("ignoring HID device, unsupported usage %04x:%04x\n", caps.UsagePage, caps.Usage); - else if (!init_controller(&controllers[i], ppd, &caps, device, detail->DevicePath)) - WARN("ignoring HID device, failed to initialize\n"); - else - continue; - - CloseHandle(device); - HidD_FreePreparsedData(ppd); - } - - SetupDiDestroyDeviceInfoList(device_info_set); - LeaveCriticalSection(&xinput_crit); -} - -static void remove_gamepad(xinput_controller *device) -{ - EnterCriticalSection(&device->crit); - - if (device->platform_private) - { - struct hid_platform_private *private = device->platform_private; - - device->platform_private = NULL; - - CloseHandle(private->device); - free(private->input_report_buf[0]); - free(private->input_report_buf[1]); - free(private->output_report_buf); - free(private->device_path); - HidD_FreePreparsedData(private->ppd); - free(private); - } - - LeaveCriticalSection(&device->crit); -} - -void HID_destroy_gamepads(void) -{ - int i; - for (i = 0; i < XUSER_MAX_COUNT; i++) - remove_gamepad(&controllers[i]); -} - -static SHORT scale_short(LONG value, const struct axis_info *axis) -{ - return ((((ULONGLONG)(value - axis->min)) * 0xffff) / axis->range) - 32768; -} - -static BYTE scale_byte(LONG value, const struct axis_info *axis) -{ - return (((ULONGLONG)(value - axis->min)) * 0xff) / axis->range; -} - -void HID_update_state(xinput_controller *device, XINPUT_STATE *state) -{ - struct hid_platform_private *private = device->platform_private; - int i; - char **report_buf = private->input_report_buf, *tmp; - ULONG report_len = private->caps.InputReportByteLength; - NTSTATUS status; - - USAGE buttons[11]; - ULONG button_length, hat_value; - LONG value; - - if (!private->enabled) - return; - - if (!HidD_GetInputReport(private->device, report_buf[0], report_len)) - { - if (GetLastError() == ERROR_ACCESS_DENIED || GetLastError() == ERROR_INVALID_HANDLE) - { - EnterCriticalSection(&xinput_crit); - remove_gamepad(device); - LeaveCriticalSection(&xinput_crit); - } - else ERR("Failed to get input report, HidD_GetInputReport failed with error %u\n", GetLastError()); - return; - } - - if (memcmp(report_buf[0], report_buf[1], report_len) != 0) - { - device->state.dwPacketNumber++; - button_length = ARRAY_SIZE(buttons); - status = HidP_GetUsages(HidP_Input, HID_USAGE_PAGE_BUTTON, 0, buttons, &button_length, private->ppd, report_buf[0], report_len); - if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsages HID_USAGE_PAGE_BUTTON returned %#x\n", status); - - device->state.Gamepad.wButtons = 0; - for (i = 0; i < button_length; i++) - { - switch (buttons[i]) - { - case 1: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_A; break; - case 2: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_B; break; - case 3: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_X; break; - case 4: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_Y; break; - case 5: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_SHOULDER; break; - case 6: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_SHOULDER; break; - case 7: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_BACK; break; - case 8: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_START; break; - case 9: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_THUMB; break; - case 10: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_THUMB; break; - case 11: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_GUIDE; break; - } - } - - status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_HATSWITCH, &hat_value, private->ppd, report_buf[0], report_len); - if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_HATSWITCH returned %#x\n", status); - else - { - switch(hat_value){ - /* 8 1 2 - * 7 0 3 - * 6 5 4 */ - case 0: - break; - case 1: - device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_UP; - break; - case 2: - device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_DPAD_RIGHT; - break; - case 3: - device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT; - break; - case 4: - device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT | XINPUT_GAMEPAD_DPAD_DOWN; - break; - case 5: - device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_DOWN; - break; - case 6: - device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_DPAD_LEFT; - break; - case 7: - device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_LEFT; - break; - case 8: - device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_DPAD_UP; - break; - } - } - - status = HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_X, &value, private->ppd, report_buf[0], report_len); - if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_X returned %#x\n", status); - else device->state.Gamepad.sThumbLX = scale_short(value, &private->lx); - - status = HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Y, &value, private->ppd, report_buf[0], report_len); - if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_Y returned %#x\n", status); - else device->state.Gamepad.sThumbLY = -scale_short(value, &private->ly) - 1; - - status = HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RX, &value, private->ppd, report_buf[0], report_len); - if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RX returned %#x\n", status); - else device->state.Gamepad.sThumbRX = scale_short(value, &private->rx); - - status = HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RY, &value, private->ppd, report_buf[0], report_len); - if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RY returned %#x\n", status); - else device->state.Gamepad.sThumbRY = -scale_short(value, &private->ry) - 1; - - status = HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RZ, &value, private->ppd, report_buf[0], report_len); - if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RZ returned %#x\n", status); - else device->state.Gamepad.bRightTrigger = scale_byte(value, &private->rtrigger); - - status = HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z, &value, private->ppd, report_buf[0], report_len); - if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_Z returned %#x\n", status); - else device->state.Gamepad.bLeftTrigger = scale_byte(value, &private->ltrigger); - } - - tmp = report_buf[0]; - report_buf[0] = report_buf[1]; - report_buf[1] = tmp; - memcpy(state, &device->state, sizeof(*state)); -} - -DWORD HID_set_state(xinput_controller* device, XINPUT_VIBRATION* state) -{ - struct hid_platform_private *private = device->platform_private; - char *output_report_buf = private->output_report_buf; - ULONG output_report_len = private->caps.OutputReportByteLength; - - if (device->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) - { - device->vibration.wLeftMotorSpeed = state->wLeftMotorSpeed; - device->vibration.wRightMotorSpeed = state->wRightMotorSpeed; - - if (private->enabled) - { - memset(output_report_buf, 0, output_report_len); - output_report_buf[0] = /* report id */ 0; - output_report_buf[1] = 0x8; - output_report_buf[3] = (BYTE)(state->wLeftMotorSpeed / 256); - output_report_buf[4] = (BYTE)(state->wRightMotorSpeed / 256); - - if (!HidD_SetOutputReport(private->device, output_report_buf, output_report_len)) - { - WARN("unable to set output report, HidD_SetOutputReport failed with error %u\n", GetLastError()); - return GetLastError(); - } - - return ERROR_SUCCESS; - } - } - - return ERROR_SUCCESS; -} - -void HID_enable(xinput_controller* device, BOOL enable) -{ - struct hid_platform_private *private = device->platform_private; - - if (device->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) - { - if (private->enabled && !enable) - { - XINPUT_VIBRATION state; - state.wLeftMotorSpeed = 0; - state.wRightMotorSpeed = 0; - HID_set_state(device, &state); - } - else if (!private->enabled && enable) - { - HID_set_state(device, &device->vibration); - } - } - - private->enabled = enable; -} diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c new file mode 100644 index 00000000000..6badd74851c --- /dev/null +++ b/dlls/xinput1_3/main.c @@ -0,0 +1,935 @@ +/* + * The Wine project - Xinput Joystick Library + * Copyright 2008 Andrew Fenn + * Copyright 2018 Aric Stewart + * Copyright 2021 RĂ©mi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <assert.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winuser.h" +#include "winreg.h" +#include "wingdi.h" +#include "winnls.h" +#include "winternl.h" + +#include "setupapi.h" +#include "devpkey.h" +#include "hidusage.h" +#include "ddk/hidsdi.h" +#include "initguid.h" +#include "devguid.h" +#include "xinput.h" + +#include "wine/debug.h" + +/* Not defined in the headers, used only by XInputGetStateEx */ +#define XINPUT_GAMEPAD_GUIDE 0x0400 + +WINE_DEFAULT_DEBUG_CHANNEL(xinput); + +typedef struct _xinput_controller +{ + CRITICAL_SECTION crit; + XINPUT_CAPABILITIES caps; + void *platform_private; /* non-NULL when device is valid; validity may be read without holding crit */ + XINPUT_STATE state; + XINPUT_GAMEPAD last_keystroke; + XINPUT_VIBRATION vibration; +} xinput_controller; + +/* xinput_crit guards controllers array */ +static CRITICAL_SECTION xinput_crit; +static CRITICAL_SECTION_DEBUG xinput_critsect_debug = +{ + 0, 0, &xinput_crit, + { &xinput_critsect_debug.ProcessLocksList, &xinput_critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": xinput_crit") } +}; +static CRITICAL_SECTION xinput_crit = { &xinput_critsect_debug, -1, 0, 0, 0, 0 }; + +static xinput_controller controllers[XUSER_MAX_COUNT]; +static CRITICAL_SECTION_DEBUG controller_critsect_debug[XUSER_MAX_COUNT] = +{ + { + 0, 0, &controllers[0].crit, + { &controller_critsect_debug[0].ProcessLocksList, &controller_critsect_debug[0].ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": controllers[0].crit") } + }, + { + 0, 0, &controllers[1].crit, + { &controller_critsect_debug[1].ProcessLocksList, &controller_critsect_debug[1].ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": controllers[1].crit") } + }, + { + 0, 0, &controllers[2].crit, + { &controller_critsect_debug[2].ProcessLocksList, &controller_critsect_debug[2].ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": controllers[2].crit") } + }, + { + 0, 0, &controllers[3].crit, + { &controller_critsect_debug[3].ProcessLocksList, &controller_critsect_debug[3].ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": controllers[3].crit") } + }, +}; + +static xinput_controller controllers[XUSER_MAX_COUNT] = +{ + {{ &controller_critsect_debug[0], -1, 0, 0, 0, 0 }}, + {{ &controller_critsect_debug[1], -1, 0, 0, 0, 0 }}, + {{ &controller_critsect_debug[2], -1, 0, 0, 0, 0 }}, + {{ &controller_critsect_debug[3], -1, 0, 0, 0, 0 }}, +}; + +struct axis_info +{ + LONG min; + LONG range; + USHORT bits; +}; + +struct hid_platform_private +{ + PHIDP_PREPARSED_DATA preparsed; + HIDP_CAPS caps; + + HANDLE device; + WCHAR *device_path; + BOOL enabled; + + char *input_report_buf[2]; + char *output_report_buf; + + struct axis_info lx, ly, ltrigger, rx, ry, rtrigger; +}; + +static DWORD last_check = 0; + +static BOOL find_opened_device(SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail, int *free_slot) +{ + struct hid_platform_private *private; + int i; + + *free_slot = XUSER_MAX_COUNT; + for (i = XUSER_MAX_COUNT; i > 0; i--) + { + if (!(private = controllers[i - 1].platform_private)) *free_slot = i - 1; + else if (!wcscmp(detail->DevicePath, private->device_path)) return TRUE; + } + return FALSE; +} + +static void MarkUsage(struct hid_platform_private *private, WORD usage, LONG min, LONG max, USHORT bits) +{ + struct axis_info info = {min, max - min, bits}; + + switch (usage) + { + case HID_USAGE_GENERIC_X: private->lx = info; break; + case HID_USAGE_GENERIC_Y: private->ly = info; break; + case HID_USAGE_GENERIC_Z: private->ltrigger = info; break; + case HID_USAGE_GENERIC_RX: private->rx = info; break; + case HID_USAGE_GENERIC_RY: private->ry = info; break; + case HID_USAGE_GENERIC_RZ: private->rtrigger = info; break; + } +} + +static BOOL VerifyGamepad(PHIDP_PREPARSED_DATA preparsed, XINPUT_CAPABILITIES *xinput_caps, + struct hid_platform_private *private) +{ + HIDP_BUTTON_CAPS *button_caps; + HIDP_VALUE_CAPS *value_caps; + NTSTATUS status; + + int i; + int button_count = 0; + + /* Count buttons */ + memset(xinput_caps, 0, sizeof(XINPUT_CAPABILITIES)); + + if (!(button_caps = malloc(sizeof(*button_caps) * private->caps.NumberInputButtonCaps))) return FALSE; + status = HidP_GetButtonCaps(HidP_Input, button_caps, &private->caps.NumberInputButtonCaps, preparsed); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetButtonCaps returned %#x\n", status); + else for (i = 0; i < private->caps.NumberInputButtonCaps; i++) + { + if (button_caps[i].UsagePage != HID_USAGE_PAGE_BUTTON) + continue; + if (button_caps[i].IsRange) + button_count = max(button_count, button_caps[i].Range.UsageMax); + else + button_count = max(button_count, button_caps[i].NotRange.Usage); + } + free(button_caps); + if (button_count < 11) + WARN("Too few buttons, continuing anyway\n"); + xinput_caps->Gamepad.wButtons = 0xffff; + + if (!(value_caps = malloc(sizeof(*value_caps) * private->caps.NumberInputValueCaps))) return FALSE; + status = HidP_GetValueCaps(HidP_Input, value_caps, &private->caps.NumberInputValueCaps, preparsed); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetValueCaps returned %#x\n", status); + else for (i = 0; i < private->caps.NumberInputValueCaps; i++) + { + if (value_caps[i].UsagePage != HID_USAGE_PAGE_GENERIC) + continue; + if (value_caps[i].IsRange) + { + int u; + for (u = value_caps[i].Range.UsageMin; u <=value_caps[i].Range.UsageMax; u++) + MarkUsage(private, u, value_caps[i].LogicalMin, value_caps[i].LogicalMax, value_caps[i].BitSize); + } + else + MarkUsage(private, value_caps[i].NotRange.Usage, value_caps[i].LogicalMin, value_caps[i].LogicalMax, value_caps[i].BitSize); + } + free(value_caps); + + if (private->ltrigger.bits) + xinput_caps->Gamepad.bLeftTrigger = (1u << (sizeof(xinput_caps->Gamepad.bLeftTrigger) + 1)) - 1; + else + WARN("Missing axis LeftTrigger\n"); + if (private->rtrigger.bits) + xinput_caps->Gamepad.bRightTrigger = (1u << (sizeof(xinput_caps->Gamepad.bRightTrigger) + 1)) - 1; + else + WARN("Missing axis RightTrigger\n"); + if (private->lx.bits) + xinput_caps->Gamepad.sThumbLX = (1u << (sizeof(xinput_caps->Gamepad.sThumbLX) + 1)) - 1; + else + WARN("Missing axis ThumbLX\n"); + if (private->ly.bits) + xinput_caps->Gamepad.sThumbLY = (1u << (sizeof(xinput_caps->Gamepad.sThumbLY) + 1)) - 1; + else + WARN("Missing axis ThumbLY\n"); + if (private->rx.bits) + xinput_caps->Gamepad.sThumbRX = (1u << (sizeof(xinput_caps->Gamepad.sThumbRX) + 1)) - 1; + else + WARN("Missing axis ThumbRX\n"); + if (private->ry.bits) + xinput_caps->Gamepad.sThumbRY = (1u << (sizeof(xinput_caps->Gamepad.sThumbRY) + 1)) - 1; + else + WARN("Missing axis ThumbRY\n"); + + xinput_caps->Type = XINPUT_DEVTYPE_GAMEPAD; + xinput_caps->SubType = XINPUT_DEVSUBTYPE_GAMEPAD; + + if (private->caps.NumberOutputValueCaps > 0) + { + xinput_caps->Flags |= XINPUT_CAPS_FFB_SUPPORTED; + xinput_caps->Vibration.wLeftMotorSpeed = 255; + xinput_caps->Vibration.wRightMotorSpeed = 255; + } + + return TRUE; +} + +static BOOL init_controller(xinput_controller *controller, PHIDP_PREPARSED_DATA preparsed, + HIDP_CAPS *caps, HANDLE device, WCHAR *device_path) +{ + size_t size; + struct hid_platform_private *private; + + if (!(private = calloc(1, sizeof(struct hid_platform_private)))) return FALSE; + private->caps = *caps; + if (!VerifyGamepad(preparsed, &controller->caps, private)) goto failed; + + TRACE("Found gamepad %s\n", debugstr_w(device_path)); + + private->preparsed = preparsed; + private->device = device; + if (!(private->input_report_buf[0] = calloc(1, private->caps.InputReportByteLength))) goto failed; + if (!(private->input_report_buf[1] = calloc(1, private->caps.InputReportByteLength))) goto failed; + if (!(private->output_report_buf = calloc(1, private->caps.OutputReportByteLength))) goto failed; + size = (lstrlenW(device_path) + 1) * sizeof(WCHAR); + if (!(private->device_path = malloc(size))) goto failed; + memcpy(private->device_path, device_path, size); + private->enabled = TRUE; + + memset(&controller->state, 0, sizeof(controller->state)); + memset(&controller->vibration, 0, sizeof(controller->vibration)); + + controller->platform_private = private; + return TRUE; + +failed: + free(private->device_path); + free(private->input_report_buf[0]); + free(private->input_report_buf[1]); + free(private->output_report_buf); + free(private); + return FALSE; +} + +static void HID_find_gamepads(void) +{ + char buffer[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + MAX_PATH * sizeof(WCHAR)]; + SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail = (SP_DEVICE_INTERFACE_DETAIL_DATA_W *)buffer; + SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)}; + PHIDP_PREPARSED_DATA preparsed; + HIDP_CAPS caps; + NTSTATUS status; + HDEVINFO set; + HANDLE device; + DWORD idx; + GUID guid; + int i; + + idx = GetTickCount(); + if ((idx - last_check) < 2000) return; + + EnterCriticalSection(&xinput_crit); + + if ((idx - last_check) < 2000) + { + LeaveCriticalSection(&xinput_crit); + return; + } + last_check = idx; + + HidD_GetHidGuid(&guid); + + set = SetupDiGetClassDevsW(&guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); + detail->cbSize = sizeof(*detail); + + idx = 0; + while (SetupDiEnumDeviceInterfaces(set, NULL, &guid, idx++, &iface)) + { + if (!SetupDiGetDeviceInterfaceDetailW(set, &iface, detail, sizeof(buffer), NULL, NULL)) + continue; + + if (!wcsstr(detail->DevicePath, L"IG_")) continue; + + if (find_opened_device(detail, &i)) continue; /* already opened */ + if (i == XUSER_MAX_COUNT) break; /* no more slots */ + + device = CreateFileW(detail->DevicePath, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); + if (device == INVALID_HANDLE_VALUE) continue; + + preparsed = NULL; + if (!HidD_GetPreparsedData(device, &preparsed)) + WARN("ignoring HID device, HidD_GetPreparsedData failed with error %u\n", GetLastError()); + else if ((status = HidP_GetCaps(preparsed, &caps)) != HIDP_STATUS_SUCCESS) + WARN("ignoring HID device, HidP_GetCaps returned %#x\n", status); + else if (caps.UsagePage != HID_USAGE_PAGE_GENERIC) + WARN("ignoring HID device, unsupported usage page %04x\n", caps.UsagePage); + else if (caps.Usage != HID_USAGE_GENERIC_GAMEPAD && caps.Usage != HID_USAGE_GENERIC_JOYSTICK && + caps.Usage != HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER) + WARN("ignoring HID device, unsupported usage %04x:%04x\n", caps.UsagePage, caps.Usage); + else if (!init_controller(&controllers[i], preparsed, &caps, device, detail->DevicePath)) + WARN("ignoring HID device, failed to initialize\n"); + else + continue; + + CloseHandle(device); + HidD_FreePreparsedData(preparsed); + } + + SetupDiDestroyDeviceInfoList(set); + LeaveCriticalSection(&xinput_crit); +} + +static void remove_gamepad(xinput_controller *device) +{ + EnterCriticalSection(&device->crit); + + if (device->platform_private) + { + struct hid_platform_private *private = device->platform_private; + + device->platform_private = NULL; + + CloseHandle(private->device); + free(private->input_report_buf[0]); + free(private->input_report_buf[1]); + free(private->output_report_buf); + free(private->device_path); + HidD_FreePreparsedData(private->preparsed); + free(private); + } + + LeaveCriticalSection(&device->crit); +} + +static void HID_destroy_gamepads(void) +{ + int i; + for (i = 0; i < XUSER_MAX_COUNT; i++) remove_gamepad(&controllers[i]); +} + +static SHORT scale_short(LONG value, const struct axis_info *axis) +{ + return ((((ULONGLONG)(value - axis->min)) * 0xffff) / axis->range) - 32768; +} + +static BYTE scale_byte(LONG value, const struct axis_info *axis) +{ + return (((ULONGLONG)(value - axis->min)) * 0xff) / axis->range; +} + +static void HID_update_state(xinput_controller *device, XINPUT_STATE *state) +{ + struct hid_platform_private *private = device->platform_private; + int i; + char **report_buf = private->input_report_buf, *tmp; + ULONG report_len = private->caps.InputReportByteLength; + NTSTATUS status; + + USAGE buttons[11]; + ULONG button_length, hat_value; + LONG value; + + if (!private->enabled) return; + + if (!HidD_GetInputReport(private->device, report_buf[0], report_len)) + { + if (GetLastError() == ERROR_ACCESS_DENIED || GetLastError() == ERROR_INVALID_HANDLE) + { + EnterCriticalSection(&xinput_crit); + remove_gamepad(device); + LeaveCriticalSection(&xinput_crit); + } + else ERR("Failed to get input report, HidD_GetInputReport failed with error %u\n", GetLastError()); + return; + } + + if (memcmp(report_buf[0], report_buf[1], report_len) != 0) + { + device->state.dwPacketNumber++; + button_length = ARRAY_SIZE(buttons); + status = HidP_GetUsages(HidP_Input, HID_USAGE_PAGE_BUTTON, 0, buttons, &button_length, private->preparsed, report_buf[0], report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsages HID_USAGE_PAGE_BUTTON returned %#x\n", status); + + device->state.Gamepad.wButtons = 0; + for (i = 0; i < button_length; i++) + { + switch (buttons[i]) + { + case 1: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_A; break; + case 2: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_B; break; + case 3: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_X; break; + case 4: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_Y; break; + case 5: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_SHOULDER; break; + case 6: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_SHOULDER; break; + case 7: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_BACK; break; + case 8: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_START; break; + case 9: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_THUMB; break; + case 10: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_THUMB; break; + case 11: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_GUIDE; break; + } + } + + status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_HATSWITCH, &hat_value, private->preparsed, report_buf[0], report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_HATSWITCH returned %#x\n", status); + else + { + switch(hat_value){ + /* 8 1 2 + * 7 0 3 + * 6 5 4 */ + case 0: + break; + case 1: + device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_UP; + break; + case 2: + device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_DPAD_RIGHT; + break; + case 3: + device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT; + break; + case 4: + device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT | XINPUT_GAMEPAD_DPAD_DOWN; + break; + case 5: + device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_DOWN; + break; + case 6: + device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_DPAD_LEFT; + break; + case 7: + device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_LEFT; + break; + case 8: + device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_DPAD_UP; + break; + } + } + + status = HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_X, &value, private->preparsed, report_buf[0], report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_X returned %#x\n", status); + else device->state.Gamepad.sThumbLX = scale_short(value, &private->lx); + + status = HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Y, &value, private->preparsed, report_buf[0], report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_Y returned %#x\n", status); + else device->state.Gamepad.sThumbLY = -scale_short(value, &private->ly) - 1; + + status = HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RX, &value, private->preparsed, report_buf[0], report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RX returned %#x\n", status); + else device->state.Gamepad.sThumbRX = scale_short(value, &private->rx); + + status = HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RY, &value, private->preparsed, report_buf[0], report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RY returned %#x\n", status); + else device->state.Gamepad.sThumbRY = -scale_short(value, &private->ry) - 1; + + status = HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RZ, &value, private->preparsed, report_buf[0], report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RZ returned %#x\n", status); + else device->state.Gamepad.bRightTrigger = scale_byte(value, &private->rtrigger); + + status = HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z, &value, private->preparsed, report_buf[0], report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_Z returned %#x\n", status); + else device->state.Gamepad.bLeftTrigger = scale_byte(value, &private->ltrigger); + } + + tmp = report_buf[0]; + report_buf[0] = report_buf[1]; + report_buf[1] = tmp; + memcpy(state, &device->state, sizeof(*state)); +} + +static DWORD HID_set_state(xinput_controller *device, XINPUT_VIBRATION *state) +{ + struct hid_platform_private *private = device->platform_private; + char *output_report_buf = private->output_report_buf; + ULONG output_report_len = private->caps.OutputReportByteLength; + + if (device->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) + { + device->vibration.wLeftMotorSpeed = state->wLeftMotorSpeed; + device->vibration.wRightMotorSpeed = state->wRightMotorSpeed; + + if (private->enabled) + { + memset(output_report_buf, 0, output_report_len); + output_report_buf[0] = /* report id */ 0; + output_report_buf[1] = 0x8; + output_report_buf[3] = (BYTE)(state->wLeftMotorSpeed / 256); + output_report_buf[4] = (BYTE)(state->wRightMotorSpeed / 256); + + if (!HidD_SetOutputReport(private->device, output_report_buf, output_report_len)) + { + WARN("unable to set output report, HidD_SetOutputReport failed with error %u\n", GetLastError()); + return GetLastError(); + } + + return ERROR_SUCCESS; + } + } + + return ERROR_SUCCESS; +} + +static void HID_enable(xinput_controller *device, BOOL enable) +{ + struct hid_platform_private *private = device->platform_private; + + if (device->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) + { + if (private->enabled && !enable) + { + XINPUT_VIBRATION state; + state.wLeftMotorSpeed = 0; + state.wRightMotorSpeed = 0; + HID_set_state(device, &state); + } + else if (!private->enabled && enable) + { + HID_set_state(device, &device->vibration); + } + } + + private->enabled = enable; +} + +static BOOL verify_and_lock_device(xinput_controller *device) +{ + if (!device->platform_private) return FALSE; + + EnterCriticalSection(&device->crit); + + if (!device->platform_private) + { + LeaveCriticalSection(&device->crit); + return FALSE; + } + + return TRUE; +} + +static void unlock_device(xinput_controller *device) +{ + LeaveCriticalSection(&device->crit); +} + +BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) +{ + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(inst); + break; + case DLL_PROCESS_DETACH: + if (reserved) break; + HID_destroy_gamepads(); + break; + } + return TRUE; +} + +void WINAPI DECLSPEC_HOTPATCH XInputEnable(BOOL enable) +{ + int index; + + TRACE("(enable %d)\n", 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 */ + HID_find_gamepads(); + + for (index = 0; index < XUSER_MAX_COUNT; index++) + { + if (!verify_and_lock_device(&controllers[index])) continue; + HID_enable(&controllers[index], enable); + unlock_device(&controllers[index]); + } +} + +DWORD WINAPI DECLSPEC_HOTPATCH XInputSetState(DWORD index, XINPUT_VIBRATION *vibration) +{ + DWORD ret; + + TRACE("(index %u, vibration %p)\n", index, vibration); + + HID_find_gamepads(); + + if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS; + if (!verify_and_lock_device(&controllers[index])) return ERROR_DEVICE_NOT_CONNECTED; + + ret = HID_set_state(&controllers[index], vibration); + + unlock_device(&controllers[index]); + + return ret; +} + +/* Some versions of SteamOverlayRenderer hot-patch XInputGetStateEx() and call + * XInputGetState() in the hook, so we need a wrapper. */ +static DWORD xinput_get_state(DWORD index, XINPUT_STATE *state) +{ + if (!state) return ERROR_BAD_ARGUMENTS; + + HID_find_gamepads(); + + if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS; + if (!verify_and_lock_device(&controllers[index])) return ERROR_DEVICE_NOT_CONNECTED; + + HID_update_state(&controllers[index], state); + + if (!controllers[index].platform_private) + { + /* update_state may have disconnected the controller */ + unlock_device(&controllers[index]); + return ERROR_DEVICE_NOT_CONNECTED; + } + + unlock_device(&controllers[index]); + + return ERROR_SUCCESS; +} + +DWORD WINAPI DECLSPEC_HOTPATCH XInputGetState(DWORD index, XINPUT_STATE *state) +{ + DWORD ret; + + TRACE("(index %u, state %p)!\n", index, state); + + ret = xinput_get_state(index, state); + if (ret != ERROR_SUCCESS) return ret; + + /* The main difference between this and the Ex version is the media guide button */ + state->Gamepad.wButtons &= ~XINPUT_GAMEPAD_GUIDE; + + return ERROR_SUCCESS; +} + +DWORD WINAPI DECLSPEC_HOTPATCH XInputGetStateEx(DWORD index, XINPUT_STATE *state) +{ + TRACE("(index %u, state %p)!\n", index, state); + + return xinput_get_state(index, state); +} + +static const int JS_STATE_OFF = 0; +static const int JS_STATE_LOW = 1; +static const int JS_STATE_HIGH = 2; + +static int joystick_state(const SHORT value) +{ + if (value > 20000) return JS_STATE_HIGH; + if (value < -20000) return JS_STATE_LOW; + return JS_STATE_OFF; +} + +static WORD js_vk_offs(const int x, const int y) +{ + if (y == JS_STATE_OFF) + { + /*if (x == JS_STATE_OFF) shouldn't get here */ + if (x == JS_STATE_LOW) return 3; /* LEFT */ + /*if (x == JS_STATE_HIGH)*/ return 2; /* RIGHT */ + } + if (y == JS_STATE_HIGH) + { + if (x == JS_STATE_OFF) return 0; /* UP */ + if (x == JS_STATE_LOW) return 4; /* UPLEFT */ + /*if (x == JS_STATE_HIGH)*/ return 5; /* UPRIGHT */ + } + /*if (y == JS_STATE_LOW)*/ + { + if (x == JS_STATE_OFF) return 1; /* DOWN */ + if (x == JS_STATE_LOW) return 7; /* DOWNLEFT */ + /*if (x == JS_STATE_HIGH)*/ return 6; /* DOWNRIGHT */ + } +} + +static DWORD check_joystick_keystroke(const DWORD index, XINPUT_KEYSTROKE *keystroke, const SHORT *cur_x, + const SHORT *cur_y, SHORT *last_x, SHORT *last_y, const WORD base_vk) +{ + int cur_vk = 0, cur_x_st, cur_y_st; + int last_vk = 0, last_x_st, last_y_st; + + cur_x_st = joystick_state(*cur_x); + cur_y_st = joystick_state(*cur_y); + if (cur_x_st || cur_y_st) + cur_vk = base_vk + js_vk_offs(cur_x_st, cur_y_st); + + last_x_st = joystick_state(*last_x); + last_y_st = joystick_state(*last_y); + if (last_x_st || last_y_st) + last_vk = base_vk + js_vk_offs(last_x_st, last_y_st); + + if (cur_vk != last_vk) + { + if (last_vk) + { + /* joystick was set, and now different. send a KEYUP event, and set + * last pos to centered, so the appropriate KEYDOWN event will be + * sent on the next call. */ + keystroke->VirtualKey = last_vk; + keystroke->Unicode = 0; /* unused */ + keystroke->Flags = XINPUT_KEYSTROKE_KEYUP; + keystroke->UserIndex = index; + keystroke->HidCode = 0; + + *last_x = 0; + *last_y = 0; + + return ERROR_SUCCESS; + } + + /* joystick was unset, send KEYDOWN. */ + keystroke->VirtualKey = cur_vk; + keystroke->Unicode = 0; /* unused */ + keystroke->Flags = XINPUT_KEYSTROKE_KEYDOWN; + keystroke->UserIndex = index; + keystroke->HidCode = 0; + + *last_x = *cur_x; + *last_y = *cur_y; + + return ERROR_SUCCESS; + } + + *last_x = *cur_x; + *last_y = *cur_y; + + return ERROR_EMPTY; +} + +static BOOL trigger_is_on(const BYTE value) +{ + return value > 30; +} + +static DWORD check_for_keystroke(const DWORD index, XINPUT_KEYSTROKE *keystroke) +{ + xinput_controller *device = &controllers[index]; + const XINPUT_GAMEPAD *cur; + DWORD ret = ERROR_EMPTY; + int i; + + static const struct + { + int mask; + WORD vk; + } buttons[] = { + { XINPUT_GAMEPAD_DPAD_UP, VK_PAD_DPAD_UP }, + { XINPUT_GAMEPAD_DPAD_DOWN, VK_PAD_DPAD_DOWN }, + { XINPUT_GAMEPAD_DPAD_LEFT, VK_PAD_DPAD_LEFT }, + { XINPUT_GAMEPAD_DPAD_RIGHT, VK_PAD_DPAD_RIGHT }, + { XINPUT_GAMEPAD_START, VK_PAD_START }, + { XINPUT_GAMEPAD_BACK, VK_PAD_BACK }, + { XINPUT_GAMEPAD_LEFT_THUMB, VK_PAD_LTHUMB_PRESS }, + { XINPUT_GAMEPAD_RIGHT_THUMB, VK_PAD_RTHUMB_PRESS }, + { XINPUT_GAMEPAD_LEFT_SHOULDER, VK_PAD_LSHOULDER }, + { XINPUT_GAMEPAD_RIGHT_SHOULDER, VK_PAD_RSHOULDER }, + { XINPUT_GAMEPAD_A, VK_PAD_A }, + { XINPUT_GAMEPAD_B, VK_PAD_B }, + { XINPUT_GAMEPAD_X, VK_PAD_X }, + { XINPUT_GAMEPAD_Y, VK_PAD_Y }, + /* note: guide button does not send an event */ + }; + + if (!verify_and_lock_device(device)) return ERROR_DEVICE_NOT_CONNECTED; + + cur = &device->state.Gamepad; + + /*** buttons ***/ + for (i = 0; i < ARRAY_SIZE(buttons); ++i) + { + if ((cur->wButtons & buttons[i].mask) ^ (device->last_keystroke.wButtons & buttons[i].mask)) + { + keystroke->VirtualKey = buttons[i].vk; + keystroke->Unicode = 0; /* unused */ + if (cur->wButtons & buttons[i].mask) + { + keystroke->Flags = XINPUT_KEYSTROKE_KEYDOWN; + device->last_keystroke.wButtons |= buttons[i].mask; + } + else + { + keystroke->Flags = XINPUT_KEYSTROKE_KEYUP; + device->last_keystroke.wButtons &= ~buttons[i].mask; + } + keystroke->UserIndex = index; + keystroke->HidCode = 0; + ret = ERROR_SUCCESS; + goto done; + } + } + + /*** triggers ***/ + if (trigger_is_on(cur->bLeftTrigger) ^ trigger_is_on(device->last_keystroke.bLeftTrigger)) + { + keystroke->VirtualKey = VK_PAD_LTRIGGER; + keystroke->Unicode = 0; /* unused */ + keystroke->Flags = trigger_is_on(cur->bLeftTrigger) ? XINPUT_KEYSTROKE_KEYDOWN : XINPUT_KEYSTROKE_KEYUP; + keystroke->UserIndex = index; + keystroke->HidCode = 0; + device->last_keystroke.bLeftTrigger = cur->bLeftTrigger; + ret = ERROR_SUCCESS; + goto done; + } + + if (trigger_is_on(cur->bRightTrigger) ^ trigger_is_on(device->last_keystroke.bRightTrigger)) + { + keystroke->VirtualKey = VK_PAD_RTRIGGER; + keystroke->Unicode = 0; /* unused */ + keystroke->Flags = trigger_is_on(cur->bRightTrigger) ? XINPUT_KEYSTROKE_KEYDOWN : XINPUT_KEYSTROKE_KEYUP; + keystroke->UserIndex = index; + keystroke->HidCode = 0; + device->last_keystroke.bRightTrigger = cur->bRightTrigger; + ret = ERROR_SUCCESS; + goto done; + } + + /*** joysticks ***/ + ret = check_joystick_keystroke(index, keystroke, &cur->sThumbLX, &cur->sThumbLY, + &device->last_keystroke.sThumbLX, + &device->last_keystroke.sThumbLY, VK_PAD_LTHUMB_UP); + if (ret == ERROR_SUCCESS) + goto done; + + ret = check_joystick_keystroke(index, keystroke, &cur->sThumbRX, &cur->sThumbRY, + &device->last_keystroke.sThumbRX, + &device->last_keystroke.sThumbRY, VK_PAD_RTHUMB_UP); + if (ret == ERROR_SUCCESS) + goto done; + +done: + unlock_device(device); + + return ret; +} + +DWORD WINAPI DECLSPEC_HOTPATCH XInputGetKeystroke(DWORD index, DWORD reserved, PXINPUT_KEYSTROKE keystroke) +{ + TRACE("(index %u, reserved %u, keystroke %p)\n", index, reserved, keystroke); + + if (index >= XUSER_MAX_COUNT && index != XUSER_INDEX_ANY) return ERROR_BAD_ARGUMENTS; + + if (index == XUSER_INDEX_ANY) + { + int i; + for (i = 0; i < XUSER_MAX_COUNT; ++i) + if (check_for_keystroke(i, keystroke) == ERROR_SUCCESS) + return ERROR_SUCCESS; + return ERROR_EMPTY; + } + + return check_for_keystroke(index, keystroke); +} + +DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilities(DWORD index, DWORD flags, XINPUT_CAPABILITIES *capabilities) +{ + TRACE("(index %u, flags 0x%x, capabilities %p)\n", index, flags, capabilities); + + HID_find_gamepads(); + + if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS; + + if (!verify_and_lock_device(&controllers[index])) return ERROR_DEVICE_NOT_CONNECTED; + + if (flags & XINPUT_FLAG_GAMEPAD && controllers[index].caps.SubType != XINPUT_DEVSUBTYPE_GAMEPAD) + { + unlock_device(&controllers[index]); + return ERROR_DEVICE_NOT_CONNECTED; + } + + memcpy(capabilities, &controllers[index].caps, sizeof(*capabilities)); + + unlock_device(&controllers[index]); + + return ERROR_SUCCESS; +} + +DWORD WINAPI DECLSPEC_HOTPATCH 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].platform_private) return ERROR_DEVICE_NOT_CONNECTED; + + return ERROR_NOT_SUPPORTED; +} + +DWORD WINAPI DECLSPEC_HOTPATCH XInputGetBatteryInformation(DWORD index, BYTE type, XINPUT_BATTERY_INFORMATION* battery) +{ + static int once; + + if (!once++) FIXME("(index %u, type %u, battery %p) Stub!\n", index, type, battery); + + if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS; + if (!controllers[index].platform_private) return ERROR_DEVICE_NOT_CONNECTED; + + return ERROR_NOT_SUPPORTED; +} diff --git a/dlls/xinput1_3/xinput_main.c b/dlls/xinput1_3/xinput_main.c deleted file mode 100644 index 2d9f54c83f2..00000000000 --- a/dlls/xinput1_3/xinput_main.c +++ /dev/null @@ -1,470 +0,0 @@ -/* - * The Wine project - Xinput Joystick Library - * Copyright 2008 Andrew Fenn - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include <assert.h> -#include <stdarg.h> -#include <string.h> - -#include "wine/debug.h" -#include "windef.h" -#include "winbase.h" -#include "winerror.h" - -#include "xinput.h" -#include "xinput_private.h" - -/* Not defined in the headers, used only by XInputGetStateEx */ -#define XINPUT_GAMEPAD_GUIDE 0x0400 - -WINE_DEFAULT_DEBUG_CHANNEL(xinput); - -/* xinput_crit guards controllers array */ -static CRITICAL_SECTION_DEBUG xinput_critsect_debug = -{ - 0, 0, &xinput_crit, - { &xinput_critsect_debug.ProcessLocksList, &xinput_critsect_debug.ProcessLocksList }, - 0, 0, { (DWORD_PTR)(__FILE__ ": xinput_crit") } -}; -CRITICAL_SECTION xinput_crit = { &xinput_critsect_debug, -1, 0, 0, 0, 0 }; - -static CRITICAL_SECTION_DEBUG controller_critsect_debug[XUSER_MAX_COUNT] = -{ - { - 0, 0, &controllers[0].crit, - { &controller_critsect_debug[0].ProcessLocksList, &controller_critsect_debug[0].ProcessLocksList }, - 0, 0, { (DWORD_PTR)(__FILE__ ": controllers[0].crit") } - }, - { - 0, 0, &controllers[1].crit, - { &controller_critsect_debug[1].ProcessLocksList, &controller_critsect_debug[1].ProcessLocksList }, - 0, 0, { (DWORD_PTR)(__FILE__ ": controllers[1].crit") } - }, - { - 0, 0, &controllers[2].crit, - { &controller_critsect_debug[2].ProcessLocksList, &controller_critsect_debug[2].ProcessLocksList }, - 0, 0, { (DWORD_PTR)(__FILE__ ": controllers[2].crit") } - }, - { - 0, 0, &controllers[3].crit, - { &controller_critsect_debug[3].ProcessLocksList, &controller_critsect_debug[3].ProcessLocksList }, - 0, 0, { (DWORD_PTR)(__FILE__ ": controllers[3].crit") } - }, -}; - -xinput_controller controllers[XUSER_MAX_COUNT] = { - {{ &controller_critsect_debug[0], -1, 0, 0, 0, 0 }}, - {{ &controller_critsect_debug[1], -1, 0, 0, 0, 0 }}, - {{ &controller_critsect_debug[2], -1, 0, 0, 0, 0 }}, - {{ &controller_critsect_debug[3], -1, 0, 0, 0, 0 }}, -}; - -static BOOL verify_and_lock_device(xinput_controller *device) -{ - if (!device->platform_private) - return FALSE; - - EnterCriticalSection(&device->crit); - - if (!device->platform_private) - { - LeaveCriticalSection(&device->crit); - return FALSE; - } - - return TRUE; -} - -static void unlock_device(xinput_controller *device) -{ - LeaveCriticalSection(&device->crit); -} - -BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) -{ - switch(reason) - { - case DLL_PROCESS_ATTACH: - DisableThreadLibraryCalls(inst); - break; - case DLL_PROCESS_DETACH: - if (reserved) break; - HID_destroy_gamepads(); - break; - } - return TRUE; -} - -void WINAPI DECLSPEC_HOTPATCH XInputEnable(BOOL enable) -{ - int index; - - TRACE("(enable %d)\n", 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 */ - HID_find_gamepads(); - - for (index = 0; index < XUSER_MAX_COUNT; index ++) - { - if (!verify_and_lock_device(&controllers[index])) continue; - HID_enable(&controllers[index], enable); - unlock_device(&controllers[index]); - } -} - -DWORD WINAPI DECLSPEC_HOTPATCH XInputSetState(DWORD index, XINPUT_VIBRATION* vibration) -{ - DWORD ret; - - TRACE("(index %u, vibration %p)\n", index, vibration); - - HID_find_gamepads(); - - if (index >= XUSER_MAX_COUNT) - return ERROR_BAD_ARGUMENTS; - if (!verify_and_lock_device(&controllers[index])) - return ERROR_DEVICE_NOT_CONNECTED; - - ret = HID_set_state(&controllers[index], vibration); - - unlock_device(&controllers[index]); - - return ret; -} - -/* Some versions of SteamOverlayRenderer hot-patch XInputGetStateEx() and call - * XInputGetState() in the hook, so we need a wrapper. */ -static DWORD xinput_get_state(DWORD index, XINPUT_STATE *state) -{ - if (!state) - return ERROR_BAD_ARGUMENTS; - - HID_find_gamepads(); - - if (index >= XUSER_MAX_COUNT) - return ERROR_BAD_ARGUMENTS; - if (!verify_and_lock_device(&controllers[index])) - return ERROR_DEVICE_NOT_CONNECTED; - - HID_update_state(&controllers[index], state); - - if (!controllers[index].platform_private) - { - /* update_state may have disconnected the controller */ - unlock_device(&controllers[index]); - return ERROR_DEVICE_NOT_CONNECTED; - } - - unlock_device(&controllers[index]); - - return ERROR_SUCCESS; -} - - -DWORD WINAPI DECLSPEC_HOTPATCH XInputGetState(DWORD index, XINPUT_STATE* state) -{ - DWORD ret; - - TRACE("(index %u, state %p)!\n", index, state); - - ret = xinput_get_state(index, state); - if (ret != ERROR_SUCCESS) - return ret; - - /* The main difference between this and the Ex version is the media guide button */ - state->Gamepad.wButtons &= ~XINPUT_GAMEPAD_GUIDE; - - return ERROR_SUCCESS; -} - -DWORD WINAPI DECLSPEC_HOTPATCH XInputGetStateEx(DWORD index, XINPUT_STATE* state) -{ - TRACE("(index %u, state %p)!\n", index, state); - - return xinput_get_state(index, state); -} - -static const int JS_STATE_OFF = 0; -static const int JS_STATE_LOW = 1; -static const int JS_STATE_HIGH = 2; - -static int joystick_state(const SHORT value) -{ - if (value > 20000) - return JS_STATE_HIGH; - if (value < -20000) - return JS_STATE_LOW; - return JS_STATE_OFF; -} - -static WORD js_vk_offs(const int x, const int y) -{ - if (y == JS_STATE_OFF) - { - /*if (x == JS_STATE_OFF) shouldn't get here */ - if (x == JS_STATE_LOW) return 3; /* LEFT */ - /*if (x == JS_STATE_HIGH)*/ return 2; /* RIGHT */ - } - if (y == JS_STATE_HIGH) - { - if (x == JS_STATE_OFF) return 0; /* UP */ - if (x == JS_STATE_LOW) return 4; /* UPLEFT */ - /*if (x == JS_STATE_HIGH)*/ return 5; /* UPRIGHT */ - } - /*if (y == JS_STATE_LOW)*/ - { - if (x == JS_STATE_OFF) return 1; /* DOWN */ - if (x == JS_STATE_LOW) return 7; /* DOWNLEFT */ - /*if (x == JS_STATE_HIGH)*/ return 6; /* DOWNRIGHT */ - } -} - -static DWORD check_joystick_keystroke(const DWORD index, XINPUT_KEYSTROKE *keystroke, - const SHORT *cur_x, const SHORT *cur_y, SHORT *last_x, SHORT *last_y, - const WORD base_vk) -{ - int cur_vk = 0, cur_x_st, cur_y_st; - int last_vk = 0, last_x_st, last_y_st; - - cur_x_st = joystick_state(*cur_x); - cur_y_st = joystick_state(*cur_y); - if (cur_x_st || cur_y_st) - cur_vk = base_vk + js_vk_offs(cur_x_st, cur_y_st); - - last_x_st = joystick_state(*last_x); - last_y_st = joystick_state(*last_y); - if (last_x_st || last_y_st) - last_vk = base_vk + js_vk_offs(last_x_st, last_y_st); - - if (cur_vk != last_vk) - { - if (last_vk) - { - /* joystick was set, and now different. send a KEYUP event, and set - * last pos to centered, so the appropriate KEYDOWN event will be - * sent on the next call. */ - keystroke->VirtualKey = last_vk; - keystroke->Unicode = 0; /* unused */ - keystroke->Flags = XINPUT_KEYSTROKE_KEYUP; - keystroke->UserIndex = index; - keystroke->HidCode = 0; - - *last_x = 0; - *last_y = 0; - - return ERROR_SUCCESS; - } - - /* joystick was unset, send KEYDOWN. */ - keystroke->VirtualKey = cur_vk; - keystroke->Unicode = 0; /* unused */ - keystroke->Flags = XINPUT_KEYSTROKE_KEYDOWN; - keystroke->UserIndex = index; - keystroke->HidCode = 0; - - *last_x = *cur_x; - *last_y = *cur_y; - - return ERROR_SUCCESS; - } - - *last_x = *cur_x; - *last_y = *cur_y; - - return ERROR_EMPTY; -} - -static BOOL trigger_is_on(const BYTE value) -{ - return value > 30; -} - -static DWORD check_for_keystroke(const DWORD index, XINPUT_KEYSTROKE *keystroke) -{ - xinput_controller *device = &controllers[index]; - const XINPUT_GAMEPAD *cur; - DWORD ret = ERROR_EMPTY; - int i; - - static const struct { - int mask; - WORD vk; - } buttons[] = { - { XINPUT_GAMEPAD_DPAD_UP, VK_PAD_DPAD_UP }, - { XINPUT_GAMEPAD_DPAD_DOWN, VK_PAD_DPAD_DOWN }, - { XINPUT_GAMEPAD_DPAD_LEFT, VK_PAD_DPAD_LEFT }, - { XINPUT_GAMEPAD_DPAD_RIGHT, VK_PAD_DPAD_RIGHT }, - { XINPUT_GAMEPAD_START, VK_PAD_START }, - { XINPUT_GAMEPAD_BACK, VK_PAD_BACK }, - { XINPUT_GAMEPAD_LEFT_THUMB, VK_PAD_LTHUMB_PRESS }, - { XINPUT_GAMEPAD_RIGHT_THUMB, VK_PAD_RTHUMB_PRESS }, - { XINPUT_GAMEPAD_LEFT_SHOULDER, VK_PAD_LSHOULDER }, - { XINPUT_GAMEPAD_RIGHT_SHOULDER, VK_PAD_RSHOULDER }, - { XINPUT_GAMEPAD_A, VK_PAD_A }, - { XINPUT_GAMEPAD_B, VK_PAD_B }, - { XINPUT_GAMEPAD_X, VK_PAD_X }, - { XINPUT_GAMEPAD_Y, VK_PAD_Y }, - /* note: guide button does not send an event */ - }; - - if (!verify_and_lock_device(device)) - return ERROR_DEVICE_NOT_CONNECTED; - - cur = &device->state.Gamepad; - - /*** buttons ***/ - for (i = 0; i < ARRAY_SIZE(buttons); ++i) - { - if ((cur->wButtons & buttons[i].mask) ^ (device->last_keystroke.wButtons & buttons[i].mask)) - { - keystroke->VirtualKey = buttons[i].vk; - keystroke->Unicode = 0; /* unused */ - if (cur->wButtons & buttons[i].mask) - { - keystroke->Flags = XINPUT_KEYSTROKE_KEYDOWN; - device->last_keystroke.wButtons |= buttons[i].mask; - } - else - { - keystroke->Flags = XINPUT_KEYSTROKE_KEYUP; - device->last_keystroke.wButtons &= ~buttons[i].mask; - } - keystroke->UserIndex = index; - keystroke->HidCode = 0; - ret = ERROR_SUCCESS; - goto done; - } - } - - /*** triggers ***/ - if (trigger_is_on(cur->bLeftTrigger) ^ trigger_is_on(device->last_keystroke.bLeftTrigger)) - { - keystroke->VirtualKey = VK_PAD_LTRIGGER; - keystroke->Unicode = 0; /* unused */ - keystroke->Flags = trigger_is_on(cur->bLeftTrigger) ? XINPUT_KEYSTROKE_KEYDOWN : XINPUT_KEYSTROKE_KEYUP; - keystroke->UserIndex = index; - keystroke->HidCode = 0; - device->last_keystroke.bLeftTrigger = cur->bLeftTrigger; - ret = ERROR_SUCCESS; - goto done; - } - - if (trigger_is_on(cur->bRightTrigger) ^ trigger_is_on(device->last_keystroke.bRightTrigger)) - { - keystroke->VirtualKey = VK_PAD_RTRIGGER; - keystroke->Unicode = 0; /* unused */ - keystroke->Flags = trigger_is_on(cur->bRightTrigger) ? XINPUT_KEYSTROKE_KEYDOWN : XINPUT_KEYSTROKE_KEYUP; - keystroke->UserIndex = index; - keystroke->HidCode = 0; - device->last_keystroke.bRightTrigger = cur->bRightTrigger; - ret = ERROR_SUCCESS; - goto done; - } - - /*** joysticks ***/ - ret = check_joystick_keystroke(index, keystroke, &cur->sThumbLX, &cur->sThumbLY, - &device->last_keystroke.sThumbLX, - &device->last_keystroke.sThumbLY, VK_PAD_LTHUMB_UP); - if (ret == ERROR_SUCCESS) - goto done; - - ret = check_joystick_keystroke(index, keystroke, &cur->sThumbRX, &cur->sThumbRY, - &device->last_keystroke.sThumbRX, - &device->last_keystroke.sThumbRY, VK_PAD_RTHUMB_UP); - if (ret == ERROR_SUCCESS) - goto done; - -done: - unlock_device(device); - - return ret; -} - -DWORD WINAPI DECLSPEC_HOTPATCH XInputGetKeystroke(DWORD index, DWORD reserved, PXINPUT_KEYSTROKE keystroke) -{ - TRACE("(index %u, reserved %u, keystroke %p)\n", index, reserved, keystroke); - - if (index >= XUSER_MAX_COUNT && index != XUSER_INDEX_ANY) - return ERROR_BAD_ARGUMENTS; - - if (index == XUSER_INDEX_ANY) - { - int i; - for (i = 0; i < XUSER_MAX_COUNT; ++i) - if (check_for_keystroke(i, keystroke) == ERROR_SUCCESS) - return ERROR_SUCCESS; - return ERROR_EMPTY; - } - - return check_for_keystroke(index, keystroke); -} - -DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilities(DWORD index, DWORD flags, XINPUT_CAPABILITIES* capabilities) -{ - TRACE("(index %u, flags 0x%x, capabilities %p)\n", index, flags, capabilities); - - HID_find_gamepads(); - - if (index >= XUSER_MAX_COUNT) - return ERROR_BAD_ARGUMENTS; - - if (!verify_and_lock_device(&controllers[index])) - return ERROR_DEVICE_NOT_CONNECTED; - - if (flags & XINPUT_FLAG_GAMEPAD && controllers[index].caps.SubType != XINPUT_DEVSUBTYPE_GAMEPAD) - { - unlock_device(&controllers[index]); - return ERROR_DEVICE_NOT_CONNECTED; - } - - memcpy(capabilities, &controllers[index].caps, sizeof(*capabilities)); - - unlock_device(&controllers[index]); - - return ERROR_SUCCESS; -} - -DWORD WINAPI DECLSPEC_HOTPATCH 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].platform_private) - return ERROR_DEVICE_NOT_CONNECTED; - - return ERROR_NOT_SUPPORTED; -} - -DWORD WINAPI DECLSPEC_HOTPATCH XInputGetBatteryInformation(DWORD index, BYTE type, XINPUT_BATTERY_INFORMATION* battery) -{ - static int once; - - if (!once++) - FIXME("(index %u, type %u, battery %p) Stub!\n", index, type, battery); - - if (index >= XUSER_MAX_COUNT) - return ERROR_BAD_ARGUMENTS; - if (!controllers[index].platform_private) - return ERROR_DEVICE_NOT_CONNECTED; - - return ERROR_NOT_SUPPORTED; -} diff --git a/dlls/xinput1_3/xinput_private.h b/dlls/xinput1_3/xinput_private.h deleted file mode 100644 index 2bf16c0ffce..00000000000 --- a/dlls/xinput1_3/xinput_private.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * The Wine project - Xinput Joystick Library - * Copyright 2018 Aric Stewart - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -typedef struct _xinput_controller -{ - CRITICAL_SECTION crit; - XINPUT_CAPABILITIES caps; - void *platform_private; /* non-NULL when device is valid; validity may be read without holding crit */ - XINPUT_STATE state; - XINPUT_GAMEPAD last_keystroke; - XINPUT_VIBRATION vibration; -} xinput_controller; - -extern CRITICAL_SECTION xinput_crit; -extern xinput_controller controllers[XUSER_MAX_COUNT]; - -void HID_find_gamepads(void) DECLSPEC_HIDDEN; -void HID_destroy_gamepads(void) DECLSPEC_HIDDEN; -void HID_update_state(xinput_controller* device, XINPUT_STATE *state) DECLSPEC_HIDDEN; -DWORD HID_set_state(xinput_controller* device, XINPUT_VIBRATION* state) DECLSPEC_HIDDEN; -void HID_enable(xinput_controller* device, BOOL enable) DECLSPEC_HIDDEN; diff --git a/dlls/xinput1_4/Makefile.in b/dlls/xinput1_4/Makefile.in index cdf4b4fed05..24b10b1e848 100644 --- a/dlls/xinput1_4/Makefile.in +++ b/dlls/xinput1_4/Makefile.in @@ -5,7 +5,6 @@ DELAYIMPORTS = hid setupapi EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \ - hid.c \ - xinput_main.c + main.c
RC_SRCS = version.rc diff --git a/dlls/xinput9_1_0/Makefile.in b/dlls/xinput9_1_0/Makefile.in index dc8739c4640..0142c95c44f 100644 --- a/dlls/xinput9_1_0/Makefile.in +++ b/dlls/xinput9_1_0/Makefile.in @@ -5,7 +5,6 @@ DELAYIMPORTS = hid setupapi EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \ - hid.c \ - xinput_main.c + main.c
RC_SRCS = version.rc