| /* | |
| * 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 <windows.h> | |
| #include <stdio.h> | |
| #include "xinput.h" | |
| #include "wine/test.h" | |
| static DWORD (WINAPI *pXInputGetState)(DWORD, XINPUT_STATE*); | |
| static DWORD (WINAPI *pXInputGetStateEx)(DWORD, XINPUT_STATE_EX*); | |
| static DWORD (WINAPI *pXInputGetCapabilities)(DWORD,DWORD,XINPUT_CAPABILITIES*); | |
| static DWORD (WINAPI *pXInputSetState)(DWORD, XINPUT_VIBRATION*); | |
| static void (WINAPI *pXInputEnable)(BOOL); | |
| static DWORD (WINAPI *pXInputGetKeystroke)(DWORD, DWORD, PXINPUT_KEYSTROKE); | |
| static DWORD (WINAPI *pXInputGetDSoundAudioDeviceGuids)(DWORD, GUID*, GUID*); | |
| static DWORD (WINAPI *pXInputGetBatteryInformation)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION*); | |
| static void dump_gamepad(XINPUT_GAMEPAD *data) | |
| { | |
| trace("-- Gamepad Variables --\n"); | |
| trace("Gamepad.wButtons: %#x\n", data->wButtons); | |
| trace("Gamepad.bLeftTrigger: %d\n", data->bLeftTrigger); | |
| trace("Gamepad.bRightTrigger: %d\n", data->bRightTrigger); | |
| trace("Gamepad.sThumbLX: %d\n", data->sThumbLX); | |
| trace("Gamepad.sThumbLY: %d\n", data->sThumbLY); | |
| trace("Gamepad.sThumbRX: %d\n", data->sThumbRX); | |
| trace("Gamepad.sThumbRY: %d\n\n", data->sThumbRY); | |
| } | |
| static void test_set_state(void) | |
| { | |
| XINPUT_VIBRATION vibrator; | |
| DWORD controllerNum; | |
| DWORD result; | |
| for(controllerNum = 0; controllerNum < XUSER_MAX_COUNT; controllerNum++) | |
| { | |
| ZeroMemory(&vibrator, sizeof(XINPUT_VIBRATION)); | |
| vibrator.wLeftMotorSpeed = 32767; | |
| vibrator.wRightMotorSpeed = 32767; | |
| result = pXInputSetState(controllerNum, &vibrator); | |
| if (result == ERROR_DEVICE_NOT_CONNECTED) continue; | |
| Sleep(250); | |
| vibrator.wLeftMotorSpeed = 0; | |
| vibrator.wRightMotorSpeed = 0; | |
| result = pXInputSetState(controllerNum, &vibrator); | |
| ok(result == ERROR_SUCCESS, "XInputSetState failed with (%d)\n", result); | |
| /* Disabling XInput here, queueing a vibration and then re-enabling XInput | |
| * is used to prove that vibrations are auto enabled when resuming XInput. | |
| * If XInputEnable(1) is removed below the vibration will never play. */ | |
| if (pXInputEnable) pXInputEnable(0); | |
| Sleep(250); | |
| vibrator.wLeftMotorSpeed = 65535; | |
| vibrator.wRightMotorSpeed = 65535; | |
| result = pXInputSetState(controllerNum, &vibrator); | |
| ok(result == ERROR_SUCCESS, "XInputSetState failed with (%d)\n", result); | |
| if (pXInputEnable) pXInputEnable(1); | |
| Sleep(250); | |
| vibrator.wLeftMotorSpeed = 0; | |
| vibrator.wRightMotorSpeed = 0; | |
| result = pXInputSetState(controllerNum, &vibrator); | |
| ok(result == ERROR_SUCCESS, "XInputSetState failed with (%d)\n", result); | |
| } | |
| result = pXInputSetState(XUSER_MAX_COUNT+1, &vibrator); | |
| ok(result == ERROR_BAD_ARGUMENTS, "XInputSetState returned (%d)\n", result); | |
| } | |
| static void test_get_state(void) | |
| { | |
| union | |
| { | |
| XINPUT_STATE state; | |
| XINPUT_STATE_EX state_ex; | |
| } xinput; | |
| DWORD controllerNum, i, result, good = XUSER_MAX_COUNT; | |
| for (i = 0; i < (pXInputGetStateEx ? 2 : 1); i++) | |
| { | |
| for (controllerNum = 0; controllerNum < XUSER_MAX_COUNT; controllerNum++) | |
| { | |
| ZeroMemory(&xinput, sizeof(xinput)); | |
| if (i == 0) | |
| result = pXInputGetState(controllerNum, &xinput.state); | |
| else | |
| result = pXInputGetStateEx(controllerNum, &xinput.state_ex); | |
| ok(result == ERROR_SUCCESS || result == ERROR_DEVICE_NOT_CONNECTED, | |
| "%s failed with (%d)\n", i == 0 ? "XInputGetState" : "XInputGetStateEx", result); | |
| if (ERROR_DEVICE_NOT_CONNECTED == result) | |
| { | |
| skip("Controller %d is not connected\n", controllerNum); | |
| continue; | |
| } | |
| trace("-- Results for controller %d --\n", controllerNum); | |
| if (i == 0) | |
| { | |
| good = controllerNum; | |
| trace("XInputGetState: %d\n", result); | |
| } | |
| else | |
| trace("XInputGetStateEx: %d\n", result); | |
| trace("State->dwPacketNumber: %d\n", xinput.state.dwPacketNumber); | |
| dump_gamepad(&xinput.state.Gamepad); | |
| } | |
| } | |
| result = pXInputGetState(XUSER_MAX_COUNT, &xinput.state); | |
| ok(result == ERROR_BAD_ARGUMENTS, "XInputGetState returned (%d)\n", result); | |
| result = pXInputGetState(XUSER_MAX_COUNT+1, &xinput.state); | |
| ok(result == ERROR_BAD_ARGUMENTS, "XInputGetState returned (%d)\n", result); | |
| if (pXInputGetStateEx) | |
| { | |
| result = pXInputGetStateEx(XUSER_MAX_COUNT, &xinput.state_ex); | |
| ok(result == ERROR_BAD_ARGUMENTS, "XInputGetState returned (%d)\n", result); | |
| result = pXInputGetStateEx(XUSER_MAX_COUNT+1, &xinput.state_ex); | |
| ok(result == ERROR_BAD_ARGUMENTS, "XInputGetState returned (%d)\n", result); | |
| } | |
| if (winetest_interactive && good < XUSER_MAX_COUNT) | |
| { | |
| DWORD now = GetTickCount(), packet = 0; | |
| XINPUT_GAMEPAD *game = &xinput.state.Gamepad; | |
| trace("You have 20 seconds to test the joystick freely\n"); | |
| do | |
| { | |
| Sleep(100); | |
| pXInputGetState(good, &xinput.state); | |
| if (xinput.state.dwPacketNumber == packet) | |
| continue; | |
| packet = xinput.state.dwPacketNumber; | |
| trace("Buttons 0x%04X Triggers %3d/%3d LT %6d/%6d RT %6d/%6d\n", | |
| game->wButtons, game->bLeftTrigger, game->bRightTrigger, | |
| game->sThumbLX, game->sThumbLY, game->sThumbRX, game->sThumbRY); | |
| } | |
| while(GetTickCount() - now < 20000); | |
| trace("Test over...\n"); | |
| } | |
| } | |
| static void test_get_keystroke(void) | |
| { | |
| XINPUT_KEYSTROKE keystroke; | |
| DWORD controllerNum; | |
| DWORD result; | |
| for(controllerNum = 0; controllerNum < XUSER_MAX_COUNT; controllerNum++) | |
| { | |
| ZeroMemory(&keystroke, sizeof(XINPUT_KEYSTROKE)); | |
| result = pXInputGetKeystroke(controllerNum, XINPUT_FLAG_GAMEPAD, &keystroke); | |
| ok(result == ERROR_EMPTY || result == ERROR_SUCCESS || result == ERROR_DEVICE_NOT_CONNECTED, | |
| "XInputGetKeystroke failed with (%d)\n", result); | |
| if (ERROR_DEVICE_NOT_CONNECTED == result) | |
| { | |
| skip("Controller %d is not connected\n", controllerNum); | |
| } | |
| } | |
| ZeroMemory(&keystroke, sizeof(XINPUT_KEYSTROKE)); | |
| result = pXInputGetKeystroke(XUSER_MAX_COUNT+1, XINPUT_FLAG_GAMEPAD, &keystroke); | |
| ok(result == ERROR_BAD_ARGUMENTS, "XInputGetKeystroke returned (%d)\n", result); | |
| } | |
| static void test_get_capabilities(void) | |
| { | |
| XINPUT_CAPABILITIES capabilities; | |
| DWORD controllerNum; | |
| DWORD result; | |
| for(controllerNum = 0; controllerNum < XUSER_MAX_COUNT; controllerNum++) | |
| { | |
| ZeroMemory(&capabilities, sizeof(XINPUT_CAPABILITIES)); | |
| result = pXInputGetCapabilities(controllerNum, XINPUT_FLAG_GAMEPAD, &capabilities); | |
| ok(result == ERROR_SUCCESS || result == ERROR_DEVICE_NOT_CONNECTED, "XInputGetCapabilities failed with (%d)\n", result); | |
| if (ERROR_DEVICE_NOT_CONNECTED == result) | |
| { | |
| skip("Controller %d is not connected\n", controllerNum); | |
| continue; | |
| } | |
| /* Important to show that the results changed between 1.3 and 1.4 XInput version */ | |
| dump_gamepad(&capabilities.Gamepad); | |
| } | |
| ZeroMemory(&capabilities, sizeof(XINPUT_CAPABILITIES)); | |
| result = pXInputGetCapabilities(XUSER_MAX_COUNT+1, XINPUT_FLAG_GAMEPAD, &capabilities); | |
| ok(result == ERROR_BAD_ARGUMENTS, "XInputGetCapabilities returned (%d)\n", result); | |
| } | |
| static void test_get_dsoundaudiodevice(void) | |
| { | |
| DWORD controllerNum; | |
| DWORD result; | |
| GUID soundRender, soundCapture; | |
| GUID testGuid = {0xFFFFFFFF, 0xFFFF, 0xFFFF, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}; | |
| GUID emptyGuid = {0x0, 0x0, 0x0, {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0}}; | |
| for(controllerNum = 0; controllerNum < XUSER_MAX_COUNT; controllerNum++) | |
| { | |
| soundRender = soundCapture = testGuid; | |
| result = pXInputGetDSoundAudioDeviceGuids(controllerNum, &soundRender, &soundCapture); | |
| ok(result == ERROR_SUCCESS || result == ERROR_DEVICE_NOT_CONNECTED, "XInputGetDSoundAudioDeviceGuids failed with (%d)\n", result); | |
| if (ERROR_DEVICE_NOT_CONNECTED == result) | |
| { | |
| skip("Controller %d is not connected\n", controllerNum); | |
| continue; | |
| } | |
| if (!IsEqualGUID(&soundRender, &emptyGuid)) | |
| ok(!IsEqualGUID(&soundRender, &testGuid), "Broken GUID returned for sound render device\n"); | |
| else | |
| trace("Headset phone not attached\n"); | |
| if (!IsEqualGUID(&soundCapture, &emptyGuid)) | |
| ok(!IsEqualGUID(&soundCapture, &testGuid), "Broken GUID returned for sound capture device\n"); | |
| else | |
| trace("Headset microphone not attached\n"); | |
| } | |
| result = pXInputGetDSoundAudioDeviceGuids(XUSER_MAX_COUNT+1, &soundRender, &soundCapture); | |
| ok(result == ERROR_BAD_ARGUMENTS, "XInputGetDSoundAudioDeviceGuids returned (%d)\n", result); | |
| } | |
| static void test_get_batteryinformation(void) | |
| { | |
| DWORD controllerNum; | |
| DWORD result; | |
| XINPUT_BATTERY_INFORMATION batteryInfo; | |
| for(controllerNum = 0; controllerNum < XUSER_MAX_COUNT; controllerNum++) | |
| { | |
| ZeroMemory(&batteryInfo, sizeof(XINPUT_BATTERY_INFORMATION)); | |
| result = pXInputGetBatteryInformation(controllerNum, BATTERY_DEVTYPE_GAMEPAD, &batteryInfo); | |
| ok(result == ERROR_SUCCESS || result == ERROR_DEVICE_NOT_CONNECTED, "XInputGetBatteryInformation failed with (%d)\n", result); | |
| if (ERROR_DEVICE_NOT_CONNECTED == result) | |
| { | |
| ok(batteryInfo.BatteryLevel == BATTERY_TYPE_DISCONNECTED, "Failed to report device as being disconnected.\n"); | |
| skip("Controller %d is not connected\n", controllerNum); | |
| } | |
| } | |
| result = pXInputGetBatteryInformation(XUSER_MAX_COUNT+1, BATTERY_DEVTYPE_GAMEPAD, &batteryInfo); | |
| ok(result == ERROR_BAD_ARGUMENTS, "XInputGetBatteryInformation returned (%d)\n", result); | |
| } | |
| START_TEST(xinput) | |
| { | |
| struct | |
| { | |
| const char *name; | |
| int version; | |
| } libs[] = { | |
| { "xinput1_1.dll", 1 }, | |
| { "xinput1_2.dll", 2 }, | |
| { "xinput1_3.dll", 3 }, | |
| { "xinput1_4.dll", 4 }, | |
| { "xinput9_1_0.dll", 0 } /* legacy for XP/Vista */ | |
| }; | |
| HMODULE hXinput; | |
| void *pXInputGetStateEx_Ordinal; | |
| int i; | |
| for (i = 0; i < sizeof(libs) / sizeof(libs[0]); i++) | |
| { | |
| hXinput = LoadLibraryA( libs[i].name ); | |
| if (!hXinput) | |
| { | |
| win_skip("Could not load %s\n", libs[i].name); | |
| continue; | |
| } | |
| trace("Testing %s\n", libs[i].name); | |
| pXInputEnable = (void*)GetProcAddress(hXinput, "XInputEnable"); | |
| pXInputSetState = (void*)GetProcAddress(hXinput, "XInputSetState"); | |
| pXInputGetState = (void*)GetProcAddress(hXinput, "XInputGetState"); | |
| pXInputGetStateEx = (void*)GetProcAddress(hXinput, "XInputGetStateEx"); /* Win >= 8 */ | |
| pXInputGetStateEx_Ordinal = (void*)GetProcAddress(hXinput, (LPCSTR) 100); | |
| pXInputGetKeystroke = (void*)GetProcAddress(hXinput, "XInputGetKeystroke"); | |
| pXInputGetCapabilities = (void*)GetProcAddress(hXinput, "XInputGetCapabilities"); | |
| pXInputGetDSoundAudioDeviceGuids = (void*)GetProcAddress(hXinput, "XInputGetDSoundAudioDeviceGuids"); | |
| pXInputGetBatteryInformation = (void*)GetProcAddress(hXinput, "XInputGetBatteryInformation"); | |
| /* XInputGetStateEx may not be present by name, use ordinal in this case */ | |
| if (!pXInputGetStateEx) | |
| pXInputGetStateEx = pXInputGetStateEx_Ordinal; | |
| test_set_state(); | |
| test_get_state(); | |
| test_get_capabilities(); | |
| if (libs[i].version != 4) | |
| test_get_dsoundaudiodevice(); | |
| else | |
| ok(!pXInputGetDSoundAudioDeviceGuids, "XInputGetDSoundAudioDeviceGuids exists in %s\n", libs[i].name); | |
| if (libs[i].version > 2) | |
| { | |
| test_get_keystroke(); | |
| test_get_batteryinformation(); | |
| ok(pXInputGetStateEx != NULL, "XInputGetStateEx not found in %s\n", libs[i].name); | |
| } | |
| else | |
| { | |
| ok(!pXInputGetKeystroke, "XInputGetKeystroke exists in %s\n", libs[i].name); | |
| ok(!pXInputGetStateEx, "XInputGetStateEx exists in %s\n", libs[i].name); | |
| ok(!pXInputGetBatteryInformation, "XInputGetBatteryInformation exists in %s\n", libs[i].name); | |
| if (libs[i].version == 0) | |
| ok(!pXInputEnable, "XInputEnable exists in %s\n", libs[i].name); | |
| } | |
| FreeLibrary(hXinput); | |
| } | |
| } |