From 01d3527065e6f29997eb0ec88e36aeeecbf8ff76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 6 Aug 2021 17:08:55 +0200 Subject: [PATCH] xinput1_3: Merge hid.c and xinput_main.c into main.c. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Alexandre Julliard --- 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 -#include -#include -#include - -#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 +#include +#include +#include + +#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 -#include -#include - -#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