1 /* 2 * The Wine project - Xinput Joystick Library 3 * Copyright 2008 Andrew Fenn 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 18 */ 19 20 #include <windows.h> 21 #include <stdio.h> 22 23 #include "xinput.h" 24 #include "wine/test.h" 25 26 static DWORD (WINAPI *pXInputGetState)(DWORD, XINPUT_STATE*); 27 static DWORD (WINAPI *pXInputGetStateEx)(DWORD, XINPUT_STATE*); 28 static DWORD (WINAPI *pXInputGetCapabilities)(DWORD,DWORD,XINPUT_CAPABILITIES*); 29 static DWORD (WINAPI *pXInputSetState)(DWORD, XINPUT_VIBRATION*); 30 static void (WINAPI *pXInputEnable)(BOOL); 31 static DWORD (WINAPI *pXInputGetKeystroke)(DWORD, DWORD, PXINPUT_KEYSTROKE); 32 static DWORD (WINAPI *pXInputGetDSoundAudioDeviceGuids)(DWORD, GUID*, GUID*); 33 static DWORD (WINAPI *pXInputGetBatteryInformation)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION*); 34 35 static void dump_gamepad(XINPUT_GAMEPAD *data) 36 { 37 trace("-- Gamepad Variables --\n"); 38 trace("Gamepad.wButtons: %#x\n", data->wButtons); 39 trace("Gamepad.bLeftTrigger: %d\n", data->bLeftTrigger); 40 trace("Gamepad.bRightTrigger: %d\n", data->bRightTrigger); 41 trace("Gamepad.sThumbLX: %d\n", data->sThumbLX); 42 trace("Gamepad.sThumbLY: %d\n", data->sThumbLY); 43 trace("Gamepad.sThumbRX: %d\n", data->sThumbRX); 44 trace("Gamepad.sThumbRY: %d\n\n", data->sThumbRY); 45 } 46 47 static void test_set_state(void) 48 { 49 XINPUT_VIBRATION vibrator; 50 DWORD controllerNum; 51 DWORD result; 52 53 for(controllerNum = 0; controllerNum < XUSER_MAX_COUNT; controllerNum++) 54 { 55 ZeroMemory(&vibrator, sizeof(XINPUT_VIBRATION)); 56 57 vibrator.wLeftMotorSpeed = 32767; 58 vibrator.wRightMotorSpeed = 32767; 59 result = pXInputSetState(controllerNum, &vibrator); 60 if (result == ERROR_DEVICE_NOT_CONNECTED) continue; 61 62 Sleep(250); 63 vibrator.wLeftMotorSpeed = 0; 64 vibrator.wRightMotorSpeed = 0; 65 result = pXInputSetState(controllerNum, &vibrator); 66 ok(result == ERROR_SUCCESS, "XInputSetState failed with (%d)\n", result); 67 68 /* Disabling XInput here, queueing a vibration and then re-enabling XInput 69 * is used to prove that vibrations are auto enabled when resuming XInput. 70 * If XInputEnable(1) is removed below the vibration will never play. */ 71 if (pXInputEnable) pXInputEnable(0); 72 73 Sleep(250); 74 vibrator.wLeftMotorSpeed = 65535; 75 vibrator.wRightMotorSpeed = 65535; 76 result = pXInputSetState(controllerNum, &vibrator); 77 ok(result == ERROR_SUCCESS, "XInputSetState failed with (%d)\n", result); 78 79 if (pXInputEnable) pXInputEnable(1); 80 Sleep(250); 81 82 vibrator.wLeftMotorSpeed = 0; 83 vibrator.wRightMotorSpeed = 0; 84 result = pXInputSetState(controllerNum, &vibrator); 85 ok(result == ERROR_SUCCESS, "XInputSetState failed with (%d)\n", result); 86 } 87 88 result = pXInputSetState(XUSER_MAX_COUNT+1, &vibrator); 89 ok(result == ERROR_BAD_ARGUMENTS, "XInputSetState returned (%d)\n", result); 90 } 91 92 static void test_get_state(void) 93 { 94 XINPUT_STATE state; 95 DWORD controllerNum, i, result, good = XUSER_MAX_COUNT; 96 97 for (i = 0; i < (pXInputGetStateEx ? 2 : 1); i++) 98 { 99 for (controllerNum = 0; controllerNum < XUSER_MAX_COUNT; controllerNum++) 100 { 101 ZeroMemory(&state, sizeof(state)); 102 103 if (i == 0) 104 result = pXInputGetState(controllerNum, &state); 105 else 106 result = pXInputGetStateEx(controllerNum, &state); 107 ok(result == ERROR_SUCCESS || result == ERROR_DEVICE_NOT_CONNECTED, 108 "%s failed with (%d)\n", i == 0 ? "XInputGetState" : "XInputGetStateEx", result); 109 110 if (ERROR_DEVICE_NOT_CONNECTED == result) 111 { 112 skip("Controller %d is not connected\n", controllerNum); 113 continue; 114 } 115 116 trace("-- Results for controller %d --\n", controllerNum); 117 if (i == 0) 118 { 119 good = controllerNum; 120 trace("XInputGetState: %d\n", result); 121 } 122 else 123 trace("XInputGetStateEx: %d\n", result); 124 trace("State->dwPacketNumber: %d\n", state.dwPacketNumber); 125 dump_gamepad(&state.Gamepad); 126 } 127 } 128 129 result = pXInputGetState(0, NULL); 130 ok(result == ERROR_BAD_ARGUMENTS, "XInputGetState returned (%d)\n", result); 131 132 result = pXInputGetState(XUSER_MAX_COUNT, &state); 133 ok(result == ERROR_BAD_ARGUMENTS, "XInputGetState returned (%d)\n", result); 134 135 result = pXInputGetState(XUSER_MAX_COUNT+1, &state); 136 ok(result == ERROR_BAD_ARGUMENTS, "XInputGetState returned (%d)\n", result); 137 if (pXInputGetStateEx) 138 { 139 result = pXInputGetStateEx(XUSER_MAX_COUNT, &state); 140 ok(result == ERROR_BAD_ARGUMENTS, "XInputGetState returned (%d)\n", result); 141 142 result = pXInputGetStateEx(XUSER_MAX_COUNT+1, &state); 143 ok(result == ERROR_BAD_ARGUMENTS, "XInputGetState returned (%d)\n", result); 144 } 145 146 if (winetest_interactive && good < XUSER_MAX_COUNT) 147 { 148 DWORD now = GetTickCount(), packet = 0; 149 XINPUT_GAMEPAD *game = &state.Gamepad; 150 151 trace("You have 20 seconds to test the joystick freely\n"); 152 do 153 { 154 Sleep(100); 155 pXInputGetState(good, &state); 156 if (state.dwPacketNumber == packet) 157 continue; 158 159 packet = state.dwPacketNumber; 160 trace("Buttons 0x%04X Triggers %3d/%3d LT %6d/%6d RT %6d/%6d\n", 161 game->wButtons, game->bLeftTrigger, game->bRightTrigger, 162 game->sThumbLX, game->sThumbLY, game->sThumbRX, game->sThumbRY); 163 } 164 while(GetTickCount() - now < 20000); 165 trace("Test over...\n"); 166 } 167 } 168 169 static void test_get_keystroke(void) 170 { 171 XINPUT_KEYSTROKE keystroke; 172 DWORD controllerNum; 173 DWORD result; 174 175 for(controllerNum = 0; controllerNum < XUSER_MAX_COUNT; controllerNum++) 176 { 177 ZeroMemory(&keystroke, sizeof(XINPUT_KEYSTROKE)); 178 179 result = pXInputGetKeystroke(controllerNum, XINPUT_FLAG_GAMEPAD, &keystroke); 180 ok(result == ERROR_EMPTY || result == ERROR_SUCCESS || result == ERROR_DEVICE_NOT_CONNECTED, 181 "XInputGetKeystroke failed with (%d)\n", result); 182 183 if (ERROR_DEVICE_NOT_CONNECTED == result) 184 { 185 skip("Controller %d is not connected\n", controllerNum); 186 } 187 } 188 189 ZeroMemory(&keystroke, sizeof(XINPUT_KEYSTROKE)); 190 result = pXInputGetKeystroke(XUSER_MAX_COUNT+1, XINPUT_FLAG_GAMEPAD, &keystroke); 191 ok(result == ERROR_BAD_ARGUMENTS, "XInputGetKeystroke returned (%d)\n", result); 192 } 193 194 static void test_get_capabilities(void) 195 { 196 XINPUT_CAPABILITIES capabilities; 197 DWORD controllerNum; 198 DWORD result; 199 200 for(controllerNum = 0; controllerNum < XUSER_MAX_COUNT; controllerNum++) 201 { 202 ZeroMemory(&capabilities, sizeof(XINPUT_CAPABILITIES)); 203 204 result = pXInputGetCapabilities(controllerNum, XINPUT_FLAG_GAMEPAD, &capabilities); 205 ok(result == ERROR_SUCCESS || result == ERROR_DEVICE_NOT_CONNECTED, "XInputGetCapabilities failed with (%d)\n", result); 206 207 if (ERROR_DEVICE_NOT_CONNECTED == result) 208 { 209 skip("Controller %d is not connected\n", controllerNum); 210 continue; 211 } 212 213 /* Important to show that the results changed between 1.3 and 1.4 XInput version */ 214 dump_gamepad(&capabilities.Gamepad); 215 } 216 217 ZeroMemory(&capabilities, sizeof(XINPUT_CAPABILITIES)); 218 result = pXInputGetCapabilities(XUSER_MAX_COUNT+1, XINPUT_FLAG_GAMEPAD, &capabilities); 219 ok(result == ERROR_BAD_ARGUMENTS, "XInputGetCapabilities returned (%d)\n", result); 220 } 221 222 static void test_get_dsoundaudiodevice(void) 223 { 224 DWORD controllerNum; 225 DWORD result; 226 GUID soundRender, soundCapture; 227 GUID testGuid = {0xFFFFFFFF, 0xFFFF, 0xFFFF, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}; 228 GUID emptyGuid = {0x0, 0x0, 0x0, {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0}}; 229 230 for(controllerNum = 0; controllerNum < XUSER_MAX_COUNT; controllerNum++) 231 { 232 soundRender = soundCapture = testGuid; 233 result = pXInputGetDSoundAudioDeviceGuids(controllerNum, &soundRender, &soundCapture); 234 ok(result == ERROR_SUCCESS || result == ERROR_DEVICE_NOT_CONNECTED, "XInputGetDSoundAudioDeviceGuids failed with (%d)\n", result); 235 236 if (ERROR_DEVICE_NOT_CONNECTED == result) 237 { 238 skip("Controller %d is not connected\n", controllerNum); 239 continue; 240 } 241 242 if (!IsEqualGUID(&soundRender, &emptyGuid)) 243 ok(!IsEqualGUID(&soundRender, &testGuid), "Broken GUID returned for sound render device\n"); 244 else 245 trace("Headset phone not attached\n"); 246 247 if (!IsEqualGUID(&soundCapture, &emptyGuid)) 248 ok(!IsEqualGUID(&soundCapture, &testGuid), "Broken GUID returned for sound capture device\n"); 249 else 250 trace("Headset microphone not attached\n"); 251 } 252 253 result = pXInputGetDSoundAudioDeviceGuids(XUSER_MAX_COUNT+1, &soundRender, &soundCapture); 254 ok(result == ERROR_BAD_ARGUMENTS, "XInputGetDSoundAudioDeviceGuids returned (%d)\n", result); 255 } 256 257 static void test_get_batteryinformation(void) 258 { 259 DWORD controllerNum; 260 DWORD result; 261 XINPUT_BATTERY_INFORMATION batteryInfo; 262 263 for(controllerNum = 0; controllerNum < XUSER_MAX_COUNT; controllerNum++) 264 { 265 ZeroMemory(&batteryInfo, sizeof(XINPUT_BATTERY_INFORMATION)); 266 267 result = pXInputGetBatteryInformation(controllerNum, BATTERY_DEVTYPE_GAMEPAD, &batteryInfo); 268 ok(result == ERROR_SUCCESS || result == ERROR_DEVICE_NOT_CONNECTED, "XInputGetBatteryInformation failed with (%d)\n", result); 269 270 if (ERROR_DEVICE_NOT_CONNECTED == result) 271 { 272 ok(batteryInfo.BatteryLevel == BATTERY_TYPE_DISCONNECTED, "Failed to report device as being disconnected.\n"); 273 skip("Controller %d is not connected\n", controllerNum); 274 } 275 } 276 277 result = pXInputGetBatteryInformation(XUSER_MAX_COUNT+1, BATTERY_DEVTYPE_GAMEPAD, &batteryInfo); 278 ok(result == ERROR_BAD_ARGUMENTS, "XInputGetBatteryInformation returned (%d)\n", result); 279 } 280 281 START_TEST(xinput) 282 { 283 struct 284 { 285 const char *name; 286 int version; 287 } libs[] = { 288 { "xinput1_1.dll", 1 }, 289 { "xinput1_2.dll", 2 }, 290 { "xinput1_3.dll", 3 }, 291 { "xinput1_4.dll", 4 }, 292 { "xinput9_1_0.dll", 0 } /* legacy for XP/Vista */ 293 }; 294 HMODULE hXinput; 295 void *pXInputGetStateEx_Ordinal; 296 int i; 297 298 for (i = 0; i < ARRAY_SIZE(libs); i++) 299 { 300 hXinput = LoadLibraryA( libs[i].name ); 301 302 if (!hXinput) 303 { 304 win_skip("Could not load %s\n", libs[i].name); 305 continue; 306 } 307 trace("Testing %s\n", libs[i].name); 308 309 pXInputEnable = (void*)GetProcAddress(hXinput, "XInputEnable"); 310 pXInputSetState = (void*)GetProcAddress(hXinput, "XInputSetState"); 311 pXInputGetState = (void*)GetProcAddress(hXinput, "XInputGetState"); 312 pXInputGetStateEx = (void*)GetProcAddress(hXinput, "XInputGetStateEx"); /* Win >= 8 */ 313 pXInputGetStateEx_Ordinal = (void*)GetProcAddress(hXinput, (LPCSTR) 100); 314 pXInputGetKeystroke = (void*)GetProcAddress(hXinput, "XInputGetKeystroke"); 315 pXInputGetCapabilities = (void*)GetProcAddress(hXinput, "XInputGetCapabilities"); 316 pXInputGetDSoundAudioDeviceGuids = (void*)GetProcAddress(hXinput, "XInputGetDSoundAudioDeviceGuids"); 317 pXInputGetBatteryInformation = (void*)GetProcAddress(hXinput, "XInputGetBatteryInformation"); 318 319 /* XInputGetStateEx may not be present by name, use ordinal in this case */ 320 if (!pXInputGetStateEx) 321 pXInputGetStateEx = pXInputGetStateEx_Ordinal; 322 323 test_set_state(); 324 test_get_state(); 325 test_get_capabilities(); 326 327 if (libs[i].version != 4) 328 test_get_dsoundaudiodevice(); 329 else 330 ok(!pXInputGetDSoundAudioDeviceGuids, "XInputGetDSoundAudioDeviceGuids exists in %s\n", libs[i].name); 331 332 if (libs[i].version > 2) 333 { 334 test_get_keystroke(); 335 test_get_batteryinformation(); 336 ok(pXInputGetStateEx != NULL, "XInputGetStateEx not found in %s\n", libs[i].name); 337 } 338 else 339 { 340 ok(!pXInputGetKeystroke, "XInputGetKeystroke exists in %s\n", libs[i].name); 341 ok(!pXInputGetStateEx, "XInputGetStateEx exists in %s\n", libs[i].name); 342 ok(!pXInputGetBatteryInformation, "XInputGetBatteryInformation exists in %s\n", libs[i].name); 343 if (libs[i].version == 0) 344 ok(!pXInputEnable, "XInputEnable exists in %s\n", libs[i].name); 345 } 346 347 FreeLibrary(hXinput); 348 } 349 } 350