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