1 /*
2  * Copyright (c) 2004-2005 Robert Reif
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #define DIRECTINPUT_VERSION 0x0700
20 
21 #define COBJMACROS
22 #include <windows.h>
23 
24 #include <math.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 
28 #include "wine/test.h"
29 #include "windef.h"
30 #include "wingdi.h"
31 #include "dinput.h"
32 
33 typedef struct tagUserData {
34     IDirectInputA *pDI;
35     DWORD version;
36 } UserData;
37 
38 static const DIOBJECTDATAFORMAT dfDIJoystickTest[] = {
39   { &GUID_XAxis,DIJOFS_X,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
40   { &GUID_YAxis,DIJOFS_Y,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
41   { &GUID_ZAxis,DIJOFS_Z,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
42   { &GUID_RxAxis,DIJOFS_RX,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
43   { &GUID_RyAxis,DIJOFS_RY,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
44   { &GUID_RzAxis,DIJOFS_RZ,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE,0},
45   { &GUID_Button,DIJOFS_BUTTON(0),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
46   { &GUID_Button,DIJOFS_BUTTON(1),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
47   { &GUID_Button,DIJOFS_BUTTON(2),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
48   { &GUID_Button,DIJOFS_BUTTON(3),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
49   { &GUID_Button,DIJOFS_BUTTON(4),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
50   { &GUID_Button,DIJOFS_BUTTON(5),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
51   { &GUID_Button,DIJOFS_BUTTON(6),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
52   { &GUID_Button,DIJOFS_BUTTON(7),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
53   { &GUID_Button,DIJOFS_BUTTON(8),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
54   { &GUID_Button,DIJOFS_BUTTON(9),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
55   { &GUID_Button,DIJOFS_BUTTON(10),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE,0},
56 };
57 
58 static const DIDATAFORMAT c_dfDIJoystickTest = {
59     sizeof(DIDATAFORMAT),
60     sizeof(DIOBJECTDATAFORMAT),
61     DIDF_ABSAXIS,
62     sizeof(DIJOYSTATE2),
63     ARRAY_SIZE(dfDIJoystickTest),
64     (LPDIOBJECTDATAFORMAT)dfDIJoystickTest
65 };
66 
67 static HWND get_hwnd(void)
68 {
69     HWND hwnd=GetForegroundWindow();
70     if (!hwnd)
71         hwnd=GetDesktopWindow();
72     return hwnd;
73 }
74 
75 typedef struct tagJoystickInfo
76 {
77     IDirectInputDeviceA *pJoystick;
78     DWORD axis;
79     DWORD pov;
80     DWORD button;
81     LONG  lMin, lMax;
82     DWORD dZone;
83 } JoystickInfo;
84 
85 static int get_refcount(IUnknown *object)
86 {
87     IUnknown_AddRef( object );
88     return IUnknown_Release( object );
89 }
90 
91 static BOOL CALLBACK EnumAxes(const DIDEVICEOBJECTINSTANCEA *pdidoi, void *pContext)
92 {
93     HRESULT hr;
94     JoystickInfo * info = pContext;
95 
96     if (IsEqualIID(&pdidoi->guidType, &GUID_XAxis) ||
97         IsEqualIID(&pdidoi->guidType, &GUID_YAxis) ||
98         IsEqualIID(&pdidoi->guidType, &GUID_ZAxis) ||
99         IsEqualIID(&pdidoi->guidType, &GUID_RxAxis) ||
100         IsEqualIID(&pdidoi->guidType, &GUID_RyAxis) ||
101         IsEqualIID(&pdidoi->guidType, &GUID_RzAxis) ||
102         IsEqualIID(&pdidoi->guidType, &GUID_Slider))
103     {
104         DIPROPRANGE diprg;
105         DIPROPDWORD dipdw;
106 
107         diprg.diph.dwSize       = sizeof(DIPROPRANGE);
108         diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
109         diprg.diph.dwHow        = DIPH_BYID;
110         diprg.diph.dwObj        = pdidoi->dwType;
111 
112         dipdw.diph.dwSize       = sizeof(dipdw);
113         dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
114         dipdw.diph.dwHow        = DIPH_BYID;
115         dipdw.diph.dwObj        = pdidoi->dwType;
116 
117         hr = IDirectInputDevice_GetProperty(info->pJoystick, DIPROP_RANGE, &diprg.diph);
118         ok(SUCCEEDED(hr), "IDirectInputDevice_GetProperty() failed: %08x\n", hr);
119         ok(info->lMin == diprg.lMin && info->lMax == diprg.lMax, "Min/Max range invalid: "
120            "expected %d..%d got %d..%d\n", info->lMin, info->lMax, diprg.lMin, diprg.lMax);
121 
122         diprg.lMin = -2000;
123         diprg.lMax = +2000;
124 
125         hr = IDirectInputDevice_SetProperty(info->pJoystick, DIPROP_RANGE, NULL);
126         ok(hr==E_INVALIDARG,"IDirectInputDevice_SetProperty() should have returned "
127            "E_INVALIDARG, returned: %08x\n", hr);
128 
129         hr = IDirectInputDevice_SetProperty(info->pJoystick, DIPROP_RANGE, &diprg.diph);
130         ok(hr==DI_OK,"IDirectInputDevice_SetProperty() failed: %08x\n", hr);
131 
132         /* dead zone */
133         hr = IDirectInputDevice_GetProperty(info->pJoystick, DIPROP_DEADZONE, &dipdw.diph);
134         ok(SUCCEEDED(hr), "IDirectInputDevice_GetProperty() failed: %08x\n", hr);
135         ok(info->dZone == dipdw.dwData, "deadzone invalid: expected %d got %d\n",
136            info->dZone, dipdw.dwData);
137 
138         dipdw.dwData = 123;
139 
140         hr = IDirectInputDevice_SetProperty(info->pJoystick, DIPROP_DEADZONE, &dipdw.diph);
141         ok(hr==DI_OK,"IDirectInputDevice_SetProperty() failed: %08x\n", hr);
142 
143         /* ensure DIDOI_ASPECTPOSITION is set for axes objects  */
144         ok(pdidoi->dwFlags & DIDOI_ASPECTPOSITION, "Missing DIDOI_ASPECTPOSITION, flags are 0x%x\n",
145            pdidoi->dwFlags);
146 
147         info->axis++;
148     } else if (IsEqualIID(&pdidoi->guidType, &GUID_POV))
149         info->pov++;
150     else if (IsEqualIID(&pdidoi->guidType, &GUID_Button))
151         info->button++;
152 
153     return DIENUM_CONTINUE;
154 }
155 
156 static const struct effect_id
157 {
158     const GUID *guid;
159     int dieft;
160     const char *name;
161 } effect_conversion[] = {
162     {&GUID_ConstantForce, DIEFT_CONSTANTFORCE, "Constant"},
163     {&GUID_RampForce,     DIEFT_RAMPFORCE,     "Ramp"},
164     {&GUID_Square,        DIEFT_PERIODIC,      "Square"},
165     {&GUID_Sine,          DIEFT_PERIODIC,      "Sine"},
166     {&GUID_Triangle,      DIEFT_PERIODIC,      "Triangle"},
167     {&GUID_SawtoothUp,    DIEFT_PERIODIC,      "Saw Tooth Up"},
168     {&GUID_SawtoothDown,  DIEFT_PERIODIC,      "Saw Tooth Down"},
169     {&GUID_Spring,        DIEFT_CONDITION,     "Spring"},
170     {&GUID_Damper,        DIEFT_CONDITION,     "Damper"},
171     {&GUID_Inertia,       DIEFT_CONDITION,     "Inertia"},
172     {&GUID_Friction,      DIEFT_CONDITION,     "Friction"},
173     {&GUID_CustomForce,   DIEFT_CUSTOMFORCE,   "Custom"}
174 };
175 
176 static const struct effect_id* effect_from_guid(const GUID *guid)
177 {
178     unsigned int i;
179     for (i = 0; i < ARRAY_SIZE(effect_conversion); i++)
180         if (IsEqualGUID(guid, effect_conversion[i].guid))
181             return &effect_conversion[i];
182     return NULL;
183 }
184 
185 struct effect_enum
186 {
187     DIEFFECT eff;
188     GUID guid;
189     int effect_count;
190     const char *effect_name;
191 };
192 
193 /* The last enumerated effect will be used for force feedback testing */
194 static BOOL CALLBACK EnumEffects(const DIEFFECTINFOA *lpef, void *ref)
195 {
196     const struct effect_id *id = effect_from_guid(&lpef->guid);
197     static union
198     {
199         DICONSTANTFORCE constant;
200         DIPERIODIC periodic;
201         DIRAMPFORCE ramp;
202         DICONDITION condition[2];
203     } specific;
204     struct effect_enum *data = ref;
205     int type = DIDFT_GETTYPE(lpef->dwEffType);
206 
207     /* Insanity check */
208     if (!id)
209     {
210         ok(0, "unsupported effect enumerated, GUID %s!\n", wine_dbgstr_guid(&lpef->guid));
211         return DIENUM_CONTINUE;
212     }
213     trace("controller supports '%s' effect\n", id->name);
214     ok(type == id->dieft, "Invalid effect type, expected 0x%x, got 0x%x\n",
215        id->dieft, lpef->dwEffType);
216 
217     /* Can't use custom for test as we don't know the data format */
218     if (type == DIEFT_CUSTOMFORCE)
219         return DIENUM_CONTINUE;
220 
221     data->effect_count++;
222     data->effect_name = id->name;
223     data->guid = *id->guid;
224 
225     ZeroMemory(&specific, sizeof(specific));
226     switch (type)
227     {
228         case DIEFT_PERIODIC:
229             data->eff.cbTypeSpecificParams  = sizeof(specific.periodic);
230             data->eff.lpvTypeSpecificParams = &specific.periodic;
231             specific.periodic.dwMagnitude = DI_FFNOMINALMAX / 2;
232             specific.periodic.dwPeriod = DI_SECONDS; /* 1 second */
233             break;
234         case DIEFT_CONSTANTFORCE:
235             data->eff.cbTypeSpecificParams  = sizeof(specific.constant);
236             data->eff.lpvTypeSpecificParams = &specific.constant;
237             specific.constant.lMagnitude = DI_FFNOMINALMAX / 2;
238             break;
239         case DIEFT_RAMPFORCE:
240             data->eff.cbTypeSpecificParams  = sizeof(specific.ramp);
241             data->eff.lpvTypeSpecificParams = &specific.ramp;
242             specific.ramp.lStart = -DI_FFNOMINALMAX / 2;
243             specific.ramp.lEnd = +DI_FFNOMINALMAX / 2;
244             break;
245         case DIEFT_CONDITION:
246         {
247             int i;
248             data->eff.cbTypeSpecificParams  = sizeof(specific.condition);
249             data->eff.lpvTypeSpecificParams = specific.condition;
250             for (i = 0; i < 2; i++)
251             {
252                 specific.condition[i].lNegativeCoefficient = -DI_FFNOMINALMAX / 2;
253                 specific.condition[i].lPositiveCoefficient = +DI_FFNOMINALMAX / 2;
254             }
255             break;
256         }
257     }
258     return DIENUM_CONTINUE;
259 }
260 
261 static const HRESULT SetCoop_null_window[16] =  {
262     E_INVALIDARG, E_INVALIDARG, E_INVALIDARG, E_INVALIDARG,
263     E_INVALIDARG, E_HANDLE,     E_HANDLE,     E_INVALIDARG,
264     E_INVALIDARG, E_HANDLE,     S_OK,         E_INVALIDARG,
265     E_INVALIDARG, E_INVALIDARG, E_INVALIDARG, E_INVALIDARG};
266 
267 static const HRESULT SetCoop_real_window[16] =  {
268     E_INVALIDARG, E_INVALIDARG, E_INVALIDARG, E_INVALIDARG,
269     E_INVALIDARG, S_OK,         S_OK,         E_INVALIDARG,
270     E_INVALIDARG, S_OK,         S_OK,         E_INVALIDARG,
271     E_INVALIDARG, E_INVALIDARG, E_INVALIDARG, E_INVALIDARG};
272 
273 static BOOL CALLBACK EnumJoysticks(const DIDEVICEINSTANCEA *lpddi, void *pvRef)
274 {
275     HRESULT hr;
276     UserData * data = pvRef;
277     IDirectInputDeviceA *pJoystick;
278     DIDATAFORMAT format;
279     DIDEVCAPS caps;
280     DIJOYSTATE2 js;
281     JoystickInfo info;
282     int i, count;
283     ULONG ref;
284     DIDEVICEINSTANCEA inst;
285     DIDEVICEINSTANCE_DX3A inst3;
286     DIPROPDWORD dipw;
287     DIPROPSTRING dps;
288     DIPROPGUIDANDPATH dpg;
289     WCHAR nameBuffer[MAX_PATH];
290     HWND hWnd = get_hwnd();
291     char oldstate[248], curstate[248];
292     DWORD axes[2] = {DIJOFS_X, DIJOFS_Y};
293     LONG  direction[2] = {0, 0};
294     LPDIRECTINPUTEFFECT effect = NULL;
295     LONG cnt1, cnt2;
296     HWND real_hWnd;
297     HINSTANCE hInstance = GetModuleHandleW(NULL);
298     DIPROPDWORD dip_gain_set, dip_gain_get;
299     struct effect_enum effect_data;
300 
301     ok(data->version > 0x0300, "Joysticks not supported in version 0x%04x\n", data->version);
302 
303     hr = IDirectInput_CreateDevice(data->pDI, &lpddi->guidInstance, NULL, NULL);
304     ok(hr==E_POINTER,"IDirectInput_CreateDevice() should have returned "
305        "E_POINTER, returned: %08x\n", hr);
306 
307     hr = IDirectInput_CreateDevice(data->pDI, NULL, &pJoystick, NULL);
308     ok(hr==E_POINTER,"IDirectInput_CreateDevice() should have returned "
309        "E_POINTER, returned: %08x\n", hr);
310 
311     hr = IDirectInput_CreateDevice(data->pDI, NULL, NULL, NULL);
312     ok(hr==E_POINTER,"IDirectInput_CreateDevice() should have returned "
313        "E_POINTER, returned: %08x\n", hr);
314 
315     hr = IDirectInput_CreateDevice(data->pDI, &lpddi->guidInstance,
316                                    &pJoystick, NULL);
317     ok(hr==DI_OK,"IDirectInput_CreateDevice() failed: %08x\n", hr);
318     if (hr!=DI_OK)
319         goto DONE;
320 
321     trace("---- Controller Information ----\n"
322           "Product Name  : %s\n"
323           "Instance Name : %s\n"
324           "devType       : 0x%08x\n"
325           "GUID Product  : %s\n"
326           "GUID Instance : %s\n"
327           "HID Page      : 0x%04x\n"
328           "HID Usage     : 0x%04x\n",
329           lpddi->tszProductName,
330           lpddi->tszInstanceName,
331           lpddi->dwDevType,
332           wine_dbgstr_guid(&lpddi->guidProduct),
333           wine_dbgstr_guid(&lpddi->guidInstance),
334           lpddi->wUsagePage,
335           lpddi->wUsage);
336 
337     /* Check if this is a HID device */
338     if (lpddi->dwDevType & DIDEVTYPE_HID)
339         ok(lpddi->wUsagePage == 0x01 && (lpddi->wUsage == 0x04 || lpddi->wUsage == 0x05),
340            "Expected a game controller HID UsagePage and Usage, got page 0x%x usage 0x%x\n",
341            lpddi->wUsagePage, lpddi->wUsage);
342 
343     /* Test for joystick ID property */
344     ZeroMemory(&dipw, sizeof(dipw));
345     dipw.diph.dwSize = sizeof(DIPROPDWORD);
346     dipw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
347     dipw.diph.dwObj = 0;
348     dipw.diph.dwHow = DIPH_DEVICE;
349 
350     hr = IDirectInputDevice_GetProperty(pJoystick, DIPROP_JOYSTICKID, &dipw.diph);
351     ok(SUCCEEDED(hr), "IDirectInputDevice_GetProperty() for DIPROP_JOYSTICKID failed\n");
352 
353     /* Test for INSTANCENAME property */
354     memset(&dps, 0, sizeof(dps));
355     dps.diph.dwSize = sizeof(DIPROPSTRING);
356     dps.diph.dwHeaderSize = sizeof(DIPROPHEADER);
357     dps.diph.dwHow = DIPH_DEVICE;
358 
359     hr = IDirectInputDevice_GetProperty(pJoystick, DIPROP_INSTANCENAME, &dps.diph);
360     ok(SUCCEEDED(hr), "IDirectInput_GetProperty() for DIPROP_INSTANCENAME failed: %08x\n", hr);
361 
362     /* Test if instance name is the same as present in DIDEVICEINSTANCE */
363     MultiByteToWideChar(CP_ACP, 0, lpddi->tszInstanceName, -1, nameBuffer, MAX_PATH);
364     ok(!lstrcmpW(nameBuffer, dps.wsz), "DIPROP_INSTANCENAME returned is wrong. Expected: %s Got: %s\n",
365                  wine_dbgstr_w(nameBuffer), wine_dbgstr_w(dps.wsz));
366 
367     hr = IDirectInputDevice_GetProperty(pJoystick, DIPROP_PRODUCTNAME, &dps.diph);
368     ok(SUCCEEDED(hr), "IDirectInput_GetProperty() for DIPROP_PRODUCTNAME failed: %08x\n", hr);
369 
370     /* Test if product name is the same as present in DIDEVICEINSTANCE */
371     MultiByteToWideChar(CP_ACP, 0, lpddi->tszProductName, -1, nameBuffer, MAX_PATH);
372     ok(!lstrcmpW(nameBuffer, dps.wsz), "DIPROP_PRODUCTNAME returned is wrong. Expected: %s Got: %s\n",
373                  wine_dbgstr_w(nameBuffer), wine_dbgstr_w(dps.wsz));
374 
375     /* Test for GUIDPATH properties */
376     memset(&dpg, 0, sizeof(dpg));
377     dpg.diph.dwSize = sizeof(DIPROPGUIDANDPATH);
378     dpg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
379     dpg.diph.dwHow = DIPH_DEVICE;
380 
381     hr = IDirectInputDevice_GetProperty(pJoystick, DIPROP_GUIDANDPATH, &dpg.diph);
382     todo_wine ok(SUCCEEDED(hr), "IDirectInput_GetProperty() for DIPROP_GUIDANDPATH failed: %08x\n", hr);
383 
384     hr = IDirectInputDevice_SetDataFormat(pJoystick, NULL);
385     ok(hr==E_POINTER,"IDirectInputDevice_SetDataFormat() should have returned "
386        "E_POINTER, returned: %08x\n", hr);
387 
388     ZeroMemory(&format, sizeof(format));
389     hr = IDirectInputDevice_SetDataFormat(pJoystick, &format);
390     ok(hr==DIERR_INVALIDPARAM,"IDirectInputDevice_SetDataFormat() should have "
391        "returned DIERR_INVALIDPARAM, returned: %08x\n", hr);
392 
393     /* try the default formats */
394     hr = IDirectInputDevice_SetDataFormat(pJoystick, &c_dfDIJoystick);
395     ok(hr==DI_OK,"IDirectInputDevice_SetDataFormat() failed: %08x\n", hr);
396 
397     hr = IDirectInputDevice_SetDataFormat(pJoystick, &c_dfDIJoystick2);
398     ok(hr==DI_OK,"IDirectInputDevice_SetDataFormat() failed: %08x\n", hr);
399 
400     /* try an alternate format */
401     hr = IDirectInputDevice_SetDataFormat(pJoystick, &c_dfDIJoystickTest);
402     ok(hr==DI_OK,"IDirectInputDevice_SetDataFormat() failed: %08x\n", hr);
403 
404     hr = IDirectInputDevice_SetDataFormat(pJoystick, &c_dfDIJoystick2);
405     ok(hr==DI_OK,"IDirectInputDevice_SetDataFormat() failed: %08x\n", hr);
406     if (hr != DI_OK)
407         goto RELEASE;
408 
409     for (i=0; i<16; i++)
410     {
411         hr = IDirectInputDevice_SetCooperativeLevel(pJoystick, NULL, i);
412         ok(hr == SetCoop_null_window[i], "SetCooperativeLevel(NULL, %d): %08x\n", i, hr);
413     }
414     for (i=0; i<16; i++)
415     {
416         hr = IDirectInputDevice_SetCooperativeLevel(pJoystick, hWnd, i);
417         ok(hr == SetCoop_real_window[i], "SetCooperativeLevel(hwnd, %d): %08x\n", i, hr);
418     }
419 
420     hr = IDirectInputDevice_SetCooperativeLevel(pJoystick, hWnd,
421                                                 DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
422     ok(hr==DI_OK,"IDirectInputDevice_SetCooperativeLevel() failed: %08x\n", hr);
423 
424     /* get capabilities */
425     hr = IDirectInputDevice_GetCapabilities(pJoystick, NULL);
426     ok(hr==E_POINTER,"IDirectInputDevice_GetCapabilities() "
427        "should have returned E_POINTER, returned: %08x\n", hr);
428 
429     ZeroMemory(&caps, sizeof(caps));
430     hr = IDirectInputDevice_GetCapabilities(pJoystick, &caps);
431     ok(hr==DIERR_INVALIDPARAM,"IDirectInputDevice_GetCapabilities() "
432        "should have returned DIERR_INVALIDPARAM, returned: %08x\n", hr);
433 
434     caps.dwSize = sizeof(caps);
435     hr = IDirectInputDevice_GetCapabilities(pJoystick, &caps);
436     ok(hr==DI_OK,"IDirectInputDevice_GetCapabilities() failed: %08x\n", hr);
437 
438     ZeroMemory(&info, sizeof(info));
439     info.pJoystick = pJoystick;
440 
441     /* default min/max limits */
442     info.lMin = 0;
443     info.lMax = 0xffff;
444     /* enumerate objects */
445     hr = IDirectInputDevice_EnumObjects(pJoystick, EnumAxes, &info, DIDFT_ALL);
446     ok(hr==DI_OK,"IDirectInputDevice_EnumObjects() failed: %08x\n", hr);
447 
448     ok(caps.dwAxes == info.axis, "Number of enumerated axes (%d) doesn't match capabilities (%d)\n", info.axis, caps.dwAxes);
449     ok(caps.dwButtons == info.button, "Number of enumerated buttons (%d) doesn't match capabilities (%d)\n", info.button, caps.dwButtons);
450     ok(caps.dwPOVs == info.pov, "Number of enumerated POVs (%d) doesn't match capabilities (%d)\n", info.pov, caps.dwPOVs);
451 
452     /* Set format and check limits again */
453     hr = IDirectInputDevice_SetDataFormat(pJoystick, &c_dfDIJoystick2);
454     ok(hr==DI_OK,"IDirectInputDevice_SetDataFormat() failed: %08x\n", hr);
455     info.lMin = -2000;
456     info.lMax = +2000;
457     info.dZone= 123;
458     hr = IDirectInputDevice_EnumObjects(pJoystick, EnumAxes, &info, DIDFT_ALL);
459     ok(hr==DI_OK,"IDirectInputDevice_EnumObjects() failed: %08x\n", hr);
460 
461     hr = IDirectInputDevice_GetDeviceInfo(pJoystick, 0);
462     ok(hr==E_POINTER, "IDirectInputDevice_GetDeviceInfo() "
463        "should have returned E_POINTER, returned: %08x\n", hr);
464 
465     ZeroMemory(&inst, sizeof(inst));
466     ZeroMemory(&inst3, sizeof(inst3));
467 
468     hr = IDirectInputDevice_GetDeviceInfo(pJoystick, &inst);
469     ok(hr==DIERR_INVALIDPARAM, "IDirectInputDevice_GetDeviceInfo() "
470        "should have returned DIERR_INVALIDPARAM, returned: %08x\n", hr);
471 
472     inst.dwSize = sizeof(inst);
473     hr = IDirectInputDevice_GetDeviceInfo(pJoystick, &inst);
474     ok(hr==DI_OK,"IDirectInputDevice_GetDeviceInfo() failed: %08x\n", hr);
475 
476     inst3.dwSize = sizeof(inst3);
477     hr = IDirectInputDevice_GetDeviceInfo(pJoystick, (DIDEVICEINSTANCEA*)&inst3);
478     ok(hr==DI_OK,"IDirectInputDevice_GetDeviceInfo() failed: %08x\n", hr);
479 
480     hr = IDirectInputDevice_Unacquire(pJoystick);
481     ok(hr == S_FALSE, "IDirectInputDevice_Unacquire() should have returned S_FALSE, got: %08x\n", hr);
482 
483     hr = IDirectInputDevice_Acquire(pJoystick);
484     ok(hr==DI_OK,"IDirectInputDevice_Acquire() failed: %08x\n", hr);
485     if (hr != DI_OK)
486         goto RELEASE;
487 
488     hr = IDirectInputDevice_Acquire(pJoystick);
489     ok(hr == S_FALSE, "IDirectInputDevice_Acquire() should have returned S_FALSE, got: %08x\n", hr);
490 
491     if (info.pov < 4)
492     {
493         hr = IDirectInputDevice_GetDeviceState(pJoystick, sizeof(DIJOYSTATE2), &js);
494         ok(hr == DI_OK, "IDirectInputDevice_GetDeviceState() failed: %08x\n", hr);
495         ok(js.rgdwPOV[3] == -1, "Default for unassigned POV should be -1 not: %d\n", js.rgdwPOV[3]);
496     }
497 
498     trace("Testing force feedback\n");
499     ZeroMemory(&effect_data, sizeof(effect_data));
500     effect_data.eff.dwSize          = sizeof(effect_data.eff);
501     effect_data.eff.dwFlags         = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
502     effect_data.eff.dwDuration      = INFINITE;
503     effect_data.eff.dwGain          = DI_FFNOMINALMAX;
504     effect_data.eff.dwTriggerButton = DIEB_NOTRIGGER;
505     effect_data.eff.cAxes           = ARRAY_SIZE(axes);
506     effect_data.eff.rgdwAxes        = axes;
507     effect_data.eff.rglDirection    = direction;
508 
509     /* Sending effects to joystick requires
510      * calling IDirectInputEffect_Initialize, which requires
511      * having exclusive access to the device, which requires
512      * - not having acquired the joystick when calling
513      *   IDirectInputDevice_SetCooperativeLevel
514      * - a visible window
515      */
516     real_hWnd = CreateWindowExA(0, "EDIT", "Test text", 0, 10, 10, 300, 300, NULL, NULL,
517                                 hInstance, NULL);
518     ok(real_hWnd!=0,"CreateWindowExA failed: %p\n", real_hWnd);
519     ShowWindow(real_hWnd, SW_SHOW);
520     hr = IDirectInputDevice_Unacquire(pJoystick);
521     ok(hr==DI_OK,"IDirectInputDevice_Unacquire() failed: %08x\n", hr);
522     hr = IDirectInputDevice_SetCooperativeLevel(pJoystick, real_hWnd,
523                                                 DISCL_EXCLUSIVE | DISCL_FOREGROUND);
524     ok(hr==DI_OK,"IDirectInputDevice_SetCooperativeLevel() failed: %08x\n", hr);
525     hr = IDirectInputDevice_Acquire(pJoystick);
526     ok(hr==DI_OK,"IDirectInputDevice_Acquire() failed: %08x\n", hr);
527 
528     cnt1 = get_refcount((IUnknown*)pJoystick);
529 
530     hr = IDirectInputDevice2_EnumEffects((IDirectInputDevice2A*)pJoystick, EnumEffects, &effect_data, DIEFT_ALL);
531     ok(hr==DI_OK,"IDirectInputDevice2_EnumEffects() failed: %08x\n", hr);
532 
533     /* If the controller does not support ANY effect use the constant effect to make
534      * CreateEffect fail but with the unsupported reason instead of invalid parameters. */
535     if (!effect_data.effect_count)
536     {
537         static DICONSTANTFORCE constant;
538         effect_data.guid = GUID_ConstantForce;
539         effect_data.eff.cbTypeSpecificParams  = sizeof(constant);
540         effect_data.eff.lpvTypeSpecificParams = &constant;
541         effect_data.effect_name = "Constant";
542         constant.lMagnitude = DI_FFNOMINALMAX / 2;
543 
544         ok(!(caps.dwFlags & DIDC_FORCEFEEDBACK), "effect count is zero but controller supports force feedback?\n");
545     }
546 
547     effect = (void *)0xdeadbeef;
548     hr = IDirectInputDevice2_CreateEffect((IDirectInputDevice2A*)pJoystick, &effect_data.guid,
549                                           &effect_data.eff, &effect, NULL);
550     if (caps.dwFlags & DIDC_FORCEFEEDBACK)
551     {
552         trace("force feedback supported with %d effects, using '%s' for test\n",
553               effect_data.effect_count, effect_data.effect_name);
554         ok(hr == DI_OK, "IDirectInputDevice_CreateEffect() failed: %08x\n", hr);
555         cnt2 = get_refcount((IUnknown*)pJoystick);
556         ok(cnt1 == cnt2, "Ref count is wrong %d != %d\n", cnt1, cnt2);
557 
558         if (effect)
559         {
560             DWORD effect_status;
561             struct DIPROPDWORD diprop_word;
562             GUID guid = {0};
563 
564             hr = IDirectInputEffect_Initialize(effect, hInstance, data->version,
565                                                &effect_data.guid);
566             ok(hr==DI_OK,"IDirectInputEffect_Initialize failed: %08x\n", hr);
567             hr = IDirectInputEffect_SetParameters(effect, &effect_data.eff, DIEP_AXES | DIEP_DIRECTION |
568                                                   DIEP_TYPESPECIFICPARAMS);
569             ok(hr==DI_OK,"IDirectInputEffect_SetParameters failed: %08x\n", hr);
570             if (hr==DI_OK) {
571                 /* Test that upload, unacquire, acquire still permits updating
572                  * uploaded effect. */
573                 hr = IDirectInputDevice_Unacquire(pJoystick);
574                 ok(hr==DI_OK,"IDirectInputDevice_Unacquire() failed: %08x\n", hr);
575                 hr = IDirectInputDevice_Acquire(pJoystick);
576                 ok(hr==DI_OK,"IDirectInputDevice_Acquire() failed: %08x\n", hr);
577                 hr = IDirectInputEffect_SetParameters(effect, &effect_data.eff, DIEP_GAIN);
578                 ok(hr==DI_OK,"IDirectInputEffect_SetParameters failed: %08x\n", hr);
579             }
580 
581             /* Check effect status.
582              * State: initially stopped
583              * start
584              * State: started
585              * unacquire, acquire, download
586              * State: stopped
587              * start
588              * State: started
589              *
590              * Shows that:
591              * - effects are stopped after Unacquire + Acquire
592              * - effects are preserved (Download + Start doesn't complain
593              *   about incomplete effect)
594              */
595             hr = IDirectInputEffect_GetEffectStatus(effect, NULL);
596             ok(hr==E_POINTER,"IDirectInputEffect_GetEffectStatus() must fail with E_POINTER, got: %08x\n", hr);
597             effect_status = 0xdeadbeef;
598             hr = IDirectInputEffect_GetEffectStatus(effect, &effect_status);
599             ok(hr==DI_OK,"IDirectInputEffect_GetEffectStatus() failed: %08x\n", hr);
600             ok(effect_status==0,"IDirectInputEffect_GetEffectStatus() reported effect as started\n");
601             hr = IDirectInputEffect_SetParameters(effect, &effect_data.eff, DIEP_START);
602             ok(hr==DI_OK,"IDirectInputEffect_SetParameters failed: %08x\n", hr);
603             hr = IDirectInputEffect_GetEffectStatus(effect, &effect_status);
604             ok(hr==DI_OK,"IDirectInputEffect_GetEffectStatus() failed: %08x\n", hr);
605             todo_wine ok(effect_status!=0,"IDirectInputEffect_GetEffectStatus() reported effect as stopped\n");
606             hr = IDirectInputDevice_Unacquire(pJoystick);
607             ok(hr==DI_OK,"IDirectInputDevice_Unacquire() failed: %08x\n", hr);
608             hr = IDirectInputDevice_Acquire(pJoystick);
609             ok(hr==DI_OK,"IDirectInputDevice_Acquire() failed: %08x\n", hr);
610             hr = IDirectInputEffect_Download(effect);
611             ok(hr==DI_OK,"IDirectInputEffect_Download() failed: %08x\n", hr);
612             hr = IDirectInputEffect_GetEffectStatus(effect, &effect_status);
613             ok(hr==DI_OK,"IDirectInputEffect_GetEffectStatus() failed: %08x\n", hr);
614             ok(effect_status==0,"IDirectInputEffect_GetEffectStatus() reported effect as started\n");
615             hr = IDirectInputEffect_Start(effect, 1, 0);
616             ok(hr==DI_OK,"IDirectInputEffect_Start() failed: %08x\n", hr);
617             hr = IDirectInputEffect_GetEffectStatus(effect, &effect_status);
618             ok(hr==DI_OK,"IDirectInputEffect_GetEffectStatus() failed: %08x\n", hr);
619             Sleep(250); /* feel the magic */
620             todo_wine ok(effect_status!=0,"IDirectInputEffect_GetEffectStatus() reported effect as stopped\n");
621             hr = IDirectInputEffect_GetEffectGuid(effect, &guid);
622             ok(hr==DI_OK,"IDirectInputEffect_GetEffectGuid() failed: %08x\n", hr);
623             ok(IsEqualGUID(&effect_data.guid, &guid), "Wrong guid returned\n");
624 
625             /* Check autocenter status
626              * State: initially stopped
627              * enable
628              * State: enabled
629              * acquire
630              * State: enabled
631              * unacquire
632              * State: enabled
633              *
634              * IDirectInputDevice2_SetProperty(DIPROP_AUTOCENTER) can only be
635              * executed when the device is released.
636              *
637              * If Executed interactively, user can feel that autocenter is
638              * only disabled when the joystick is acquired.
639              */
640             diprop_word.diph.dwSize = sizeof(diprop_word);
641             diprop_word.diph.dwHeaderSize = sizeof(diprop_word.diph);
642             diprop_word.diph.dwObj = 0;
643             diprop_word.diph.dwHow = DIPH_DEVICE;
644             hr = IDirectInputDevice_Unacquire(pJoystick);
645             ok(hr==DI_OK,"IDirectInputDevice_Unacquire() failed: %08x\n", hr);
646             hr = IDirectInputDevice2_GetProperty(pJoystick, DIPROP_AUTOCENTER, &diprop_word.diph);
647             ok(hr==DI_OK,"IDirectInputDevice2_GetProperty() failed: %08x\n", hr);
648             ok(diprop_word.dwData==DIPROPAUTOCENTER_ON,"IDirectInputDevice2_GetProperty() reported autocenter as disabled\n");
649             diprop_word.dwData = DIPROPAUTOCENTER_OFF;
650             hr = IDirectInputDevice2_SetProperty(pJoystick, DIPROP_AUTOCENTER, &diprop_word.diph);
651             ok(hr==DI_OK,"IDirectInputDevice2_SetProperty() failed: %08x\n", hr);
652             hr = IDirectInputDevice2_GetProperty(pJoystick, DIPROP_AUTOCENTER, &diprop_word.diph);
653             ok(hr==DI_OK,"IDirectInputDevice2_GetProperty() failed: %08x\n", hr);
654             ok(diprop_word.dwData==DIPROPAUTOCENTER_OFF,"IDirectInputDevice2_GetProperty() reported autocenter as enabled\n");
655             if (winetest_interactive) {
656                 trace("Acquiring in 2s, autocenter will be disabled.\n");
657                 Sleep(2000);
658             }
659             hr = IDirectInputDevice_Acquire(pJoystick);
660             ok(hr==DI_OK,"IDirectInputDevice_Acquire() failed: %08x\n", hr);
661             if (winetest_interactive)
662                 trace("Acquired.\n");
663             hr = IDirectInputDevice2_GetProperty(pJoystick, DIPROP_AUTOCENTER, &diprop_word.diph);
664             ok(hr==DI_OK,"IDirectInputDevice2_GetProperty() failed: %08x\n", hr);
665             ok(diprop_word.dwData==DIPROPAUTOCENTER_OFF,"IDirectInputDevice2_GetProperty() reported autocenter as enabled\n");
666             if (winetest_interactive) {
667                 trace("Releasing in 2s, autocenter will be re-enabled.\n");
668                 Sleep(2000);
669             }
670             hr = IDirectInputDevice_Unacquire(pJoystick);
671             ok(hr==DI_OK,"IDirectInputDevice_Unacquire() failed: %08x\n", hr);
672             if (winetest_interactive)
673                 trace("Released\n");
674             hr = IDirectInputDevice2_GetProperty(pJoystick, DIPROP_AUTOCENTER, &diprop_word.diph);
675             ok(hr==DI_OK,"IDirectInputDevice2_GetProperty() failed: %08x\n", hr);
676             ok(diprop_word.dwData==DIPROPAUTOCENTER_OFF,"IDirectInputDevice2_GetProperty() reported autocenter as enabled\n");
677             hr = IDirectInputDevice_Acquire(pJoystick);
678             ok(hr==DI_OK,"IDirectInputDevice_Acquire() failed: %08x\n", hr);
679             hr = IDirectInputDevice2_GetProperty(pJoystick, DIPROP_AUTOCENTER, &diprop_word.diph);
680             ok(hr==DI_OK,"IDirectInputDevice2_GetProperty() failed: %08x\n", hr);
681 
682             /* Device gain (DIPROP_FFGAIN).
683              * From MSDN:
684              *  0..10000 range, otherwise DIERR_INVALIDPARAM.
685              *  Can be changed even if device is acquired.
686              * Difference found by tests:
687              *  <0 is refused, >10000 is accepted
688              */
689             dip_gain_set.diph.dwSize       = sizeof(DIPROPDWORD);
690             dip_gain_set.diph.dwHeaderSize = sizeof(DIPROPHEADER);
691             dip_gain_set.diph.dwObj        = 0;
692             dip_gain_set.diph.dwHow        = DIPH_DEVICE;
693             dip_gain_get.diph.dwSize       = sizeof(DIPROPDWORD);
694             dip_gain_get.diph.dwHeaderSize = sizeof(DIPROPHEADER);
695             dip_gain_get.diph.dwObj        = 0;
696             dip_gain_get.diph.dwHow        = DIPH_DEVICE;
697             dip_gain_get.dwData            = 0;
698 
699             /* Test device is acquisition (non)impact. */
700             hr = IDirectInputDevice_Unacquire(pJoystick);
701             ok(hr == DI_OK, "IDirectInputDevice_Unacquire() should have returned S_FALSE, got: %08x\n", hr);
702             dip_gain_set.dwData = 1;
703             hr = IDirectInputDevice_SetProperty(pJoystick, DIPROP_FFGAIN, &dip_gain_set.diph);
704             ok(hr==DI_OK, "IDirectInputDevice_SetProperty() failed: %08x\n", hr);
705             hr = IDirectInputDevice_GetProperty(pJoystick, DIPROP_FFGAIN, &dip_gain_get.diph);
706             ok(hr==DI_OK, "IDirectInputDevice_GetProperty() failed: %08x\n", hr);
707             ok(dip_gain_get.dwData==dip_gain_set.dwData, "Gain not updated: %i\n", dip_gain_get.dwData);
708             hr = IDirectInputDevice_Acquire(pJoystick);
709             ok(hr==DI_OK,"IDirectInputDevice_Acquire() failed: %08x\n", hr);
710             dip_gain_set.dwData = 2;
711             hr = IDirectInputDevice_SetProperty(pJoystick, DIPROP_FFGAIN, &dip_gain_set.diph);
712             ok(hr==DI_OK, "IDirectInputDevice_SetProperty() failed: %08x\n", hr);
713             hr = IDirectInputDevice_GetProperty(pJoystick, DIPROP_FFGAIN, &dip_gain_get.diph);
714             ok(hr==DI_OK, "IDirectInputDevice_GetProperty() failed: %08x\n", hr);
715             ok(dip_gain_get.dwData==dip_gain_set.dwData, "Gain not updated: %i\n", dip_gain_get.dwData);
716             /* Test range and internal clamping. */
717             dip_gain_set.dwData = -1;
718             hr = IDirectInputDevice_SetProperty(pJoystick, DIPROP_FFGAIN, &dip_gain_set.diph);
719             todo_wine ok(hr==DIERR_INVALIDPARAM, "IDirectInputDevice_SetProperty() should have returned %08x: %08x\n", DIERR_INVALIDPARAM, hr);
720             dip_gain_set.dwData = 0;
721             hr = IDirectInputDevice_SetProperty(pJoystick, DIPROP_FFGAIN, &dip_gain_set.diph);
722             ok(hr==DI_OK, "IDirectInputDevice_SetProperty() failed: %08x\n", hr);
723             hr = IDirectInputDevice_GetProperty(pJoystick, DIPROP_FFGAIN, &dip_gain_get.diph);
724             ok(hr==DI_OK, "IDirectInputDevice_GetProperty() failed: %08x\n", hr);
725             ok(dip_gain_get.dwData==dip_gain_set.dwData, "Gain not updated: %i\n", dip_gain_get.dwData);
726             dip_gain_set.dwData = 10000;
727             hr = IDirectInputDevice_SetProperty(pJoystick, DIPROP_FFGAIN, &dip_gain_set.diph);
728             ok(hr==DI_OK, "IDirectInputDevice_SetProperty() failed: %08x\n", hr);
729             hr = IDirectInputDevice_GetProperty(pJoystick, DIPROP_FFGAIN, &dip_gain_get.diph);
730             ok(hr==DI_OK, "IDirectInputDevice_GetProperty() failed: %08x\n", hr);
731             ok(dip_gain_get.dwData==dip_gain_set.dwData, "Gain not updated: %i\n", dip_gain_get.dwData);
732             /* WARNING: This call succeeds, on the contrary of what is stated on MSDN. */
733             dip_gain_set.dwData = 10001;
734             hr = IDirectInputDevice_SetProperty(pJoystick, DIPROP_FFGAIN, &dip_gain_set.diph);
735             ok(hr==DI_OK, "IDirectInputDevice_SetProperty() failed: %08x\n", hr);
736             hr = IDirectInputDevice_GetProperty(pJoystick, DIPROP_FFGAIN, &dip_gain_get.diph);
737             ok(hr==DI_OK, "IDirectInputDevice_GetProperty() failed: %08x\n", hr);
738             ok(dip_gain_get.dwData==dip_gain_set.dwData, "Gain not updated: %i\n", dip_gain_get.dwData);
739 
740             /* Test SendForceFeedbackCommand
741              * DISFFC_STOPALL - Should stop effects only
742              * DISFFC_RESET - Should stop effects and unload them (NOT release them)
743              * Tests for game Odallus (bug 41623) */
744             hr = IDirectInputDevice2_SendForceFeedbackCommand((IDirectInputDevice2A*)pJoystick, 0);
745             ok(hr==DIERR_INVALIDPARAM, "IDirectInputDevice_SendForceFeedbackCommand() failed: %08x\n", hr);
746             hr = IDirectInputDevice2_SendForceFeedbackCommand((IDirectInputDevice2A*)pJoystick, 0xFF);
747             ok(hr==DIERR_INVALIDPARAM, "IDirectInputDevice_SendForceFeedbackCommand() failed: %08x\n", hr);
748 
749             hr = IDirectInputEffect_Download(effect);
750             ok(hr==DI_OK,"IDirectInputEffect_Download() failed: %08x\n", hr);
751 
752             /* Send STOPALL and prove that the effect can still be started */
753             hr = IDirectInputDevice2_SendForceFeedbackCommand((IDirectInputDevice2A*)pJoystick, DISFFC_STOPALL);
754             ok(hr==DI_OK, "IDirectInputDevice_SendForceFeedbackCommand() failed: %08x\n", hr);
755             hr = IDirectInputEffect_GetEffectStatus(effect, &effect_status);
756             ok(hr==DI_OK,"IDirectInputEffect_GetEffectStatus() failed: %08x\n", hr);
757             ok(effect_status==0,"IDirectInputEffect_GetEffectStatus() reported effect as started\n");
758             hr = IDirectInputEffect_Start(effect, 1, 0);
759             ok(hr==DI_OK,"IDirectInputEffect_Start() failed: %08x\n", hr);
760             hr = IDirectInputEffect_GetEffectGuid(effect, &guid);
761             ok(hr==DI_OK,"IDirectInputEffect_GetEffectGuid() failed: %08x\n", hr);
762             ok(IsEqualGUID(&effect_data.guid, &guid), "Wrong guid returned\n");
763 
764             /* Send RESET and prove that we can still manipulate the effect, thus not released */
765             hr = IDirectInputDevice2_SendForceFeedbackCommand((IDirectInputDevice2A*)pJoystick, DISFFC_RESET);
766             ok(hr==DI_OK, "IDirectInputDevice_SendForceFeedbackCommand() failed: %08x\n", hr);
767             hr = IDirectInputEffect_GetEffectStatus(effect, &effect_status);
768             ok(hr==DIERR_NOTDOWNLOADED,"IDirectInputEffect_GetEffectStatus() failed: %08x\n", hr);
769             hr = IDirectInputEffect_Download(effect);
770             ok(hr==DI_OK,"IDirectInputEffect_Download() failed: %08x\n", hr);
771             hr = IDirectInputEffect_GetEffectStatus(effect, &effect_status);
772             ok(hr==DI_OK,"IDirectInputEffect_GetEffectStatus() failed: %08x\n", hr);
773             ok(effect_status==0,"IDirectInputEffect_GetEffectStatus() reported effect as started\n");
774             hr = IDirectInputEffect_Start(effect, 1, 0);
775             ok(hr==DI_OK,"IDirectInputEffect_Start() failed: %08x\n", hr);
776             hr = IDirectInputEffect_GetEffectGuid(effect, &guid);
777             ok(hr==DI_OK,"IDirectInputEffect_GetEffectGuid() failed: %08x\n", hr);
778             ok(IsEqualGUID(&effect_data.guid, &guid), "Wrong guid returned\n");
779 
780             ref = IUnknown_Release(effect);
781             ok(ref == 0, "IDirectInputDevice_Release() reference count = %d\n", ref);
782         }
783         cnt1 = get_refcount((IUnknown*)pJoystick);
784         ok(cnt1 == cnt2, "Ref count is wrong %d != %d\n", cnt1, cnt2);
785     }
786     /* No force feedback support, CreateEffect is supposed to fail. Fairy Bloom Freesia
787      * calls CreateEffect without checking the DIDC_FORCEFEEDBACK. It expects the correct
788      * error return to determine if force feedback is unsupported. */
789     else
790     {
791         trace("No force feedback support\n");
792         ok(hr==DIERR_UNSUPPORTED, "IDirectInputDevice_CreateEffect() must fail with DIERR_UNSUPPORTED, got: %08x\n", hr);
793         ok(effect == NULL, "effect must be NULL, got %p\n", effect);
794     }
795 
796     /* Before destroying the window, release joystick to revert to
797      * non-exclusive, background cooperative level. */
798     hr = IDirectInputDevice_Unacquire(pJoystick);
799     ok(hr==DI_OK,"IDirectInputDevice_Unacquire() failed: %08x\n", hr);
800     hr = IDirectInputDevice_SetCooperativeLevel(pJoystick, hWnd,
801                                                 DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
802     ok(hr==DI_OK,"IDirectInputDevice_SetCooperativeLevel() failed: %08x\n", hr);
803     DestroyWindow (real_hWnd);
804     hr = IDirectInputDevice_Acquire(pJoystick);
805     ok(hr==DI_OK,"IDirectInputDevice_Acquire() failed: %08x\n", hr);
806 
807     if (winetest_interactive) {
808         trace("You have 30 seconds to test all axes, sliders, POVs and buttons\n");
809         count = 300;
810     } else
811         count = 1;
812 
813     trace("\n");
814     oldstate[0]='\0';
815     for (i = 0; i < count; i++) {
816         hr = IDirectInputDevice_GetDeviceState(pJoystick, sizeof(DIJOYSTATE2), &js);
817         ok(hr==DI_OK,"IDirectInputDevice_GetDeviceState() failed: %08x\n", hr);
818         if (hr != DI_OK)
819             break;
820         sprintf(curstate, "X%5d Y%5d Z%5d Rx%5d Ry%5d Rz%5d "
821               "S0%5d S1%5d POV0%5d POV1%5d POV2%5d POV3%5d "
822               "B %d %d %d %d %d %d %d %d %d %d %d %d\n",
823               js.lX, js.lY, js.lZ, js.lRx, js.lRy, js.lRz,
824               js.rglSlider[0], js.rglSlider[1],
825               js.rgdwPOV[0], js.rgdwPOV[1], js.rgdwPOV[2], js.rgdwPOV[3],
826               js.rgbButtons[0]>>7, js.rgbButtons[1]>>7, js.rgbButtons[2]>>7,
827               js.rgbButtons[3]>>7, js.rgbButtons[4]>>7, js.rgbButtons[5]>>7,
828               js.rgbButtons[6]>>7, js.rgbButtons[7]>>7, js.rgbButtons[8]>>7,
829               js.rgbButtons[9]>>7, js.rgbButtons[10]>>7, js.rgbButtons[11]>>7);
830         if (strcmp(oldstate, curstate) != 0)
831         {
832             trace("%s\n", curstate);
833             strcpy(oldstate, curstate);
834         }
835         Sleep(100);
836     }
837     trace("\n");
838 
839     hr = IDirectInputDevice_Unacquire(pJoystick);
840     ok(hr==DI_OK,"IDirectInputDevice_Unacquire() failed: %08x\n", hr);
841 
842 RELEASE:
843     ref = IDirectInputDevice_Release(pJoystick);
844     ok(ref==0,"IDirectInputDevice_Release() reference count = %d\n", ref);
845 
846 DONE:
847     return DIENUM_CONTINUE;
848 }
849 
850 static void joystick_tests(DWORD version)
851 {
852     HRESULT hr;
853     IDirectInputA *pDI;
854     ULONG ref;
855     HINSTANCE hInstance = GetModuleHandleW(NULL);
856 
857     trace("-- Testing Direct Input Version 0x%04x --\n", version);
858     hr = DirectInputCreateA(hInstance, version, &pDI, NULL);
859     ok(hr==DI_OK||hr==DIERR_OLDDIRECTINPUTVERSION, "DirectInputCreateA() failed: %08x\n", hr);
860     if (hr==DI_OK && pDI!=0) {
861         UserData data;
862         data.pDI = pDI;
863         data.version = version;
864         hr = IDirectInput_EnumDevices(pDI, DIDEVTYPE_JOYSTICK, EnumJoysticks,
865                                       &data, DIEDFL_ALLDEVICES);
866         ok(hr==DI_OK,"IDirectInput_EnumDevices() failed: %08x\n", hr);
867         ref = IDirectInput_Release(pDI);
868         ok(ref==0,"IDirectInput_Release() reference count = %d\n", ref);
869     } else if (hr==DIERR_OLDDIRECTINPUTVERSION)
870         trace("  Version Not Supported\n");
871 }
872 
873 START_TEST(joystick)
874 {
875     CoInitialize(NULL);
876 
877     joystick_tests(0x0700);
878     joystick_tests(0x0500);
879     joystick_tests(0x0300);
880 
881     CoUninitialize();
882 }
883