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