1 /*
2  * Copyright (c) 2006 Vitaliy Margolen
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 #include "precomp.h"
20 
21 static const DIOBJECTDATAFORMAT obj_data_format[] = {
22   { &GUID_YAxis, 16, DIDFT_OPTIONAL|DIDFT_AXIS  |DIDFT_MAKEINSTANCE(1), 0},
23   { &GUID_Button,15, DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_MAKEINSTANCE(3), 0},
24   { &GUID_Key,    0, DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_MAKEINSTANCE(16),0},
25   { &GUID_Key,    1, DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_MAKEINSTANCE(17),0},
26   { &GUID_Key,    2, DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_MAKEINSTANCE(18),0},
27   { &GUID_Key,    3, DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_MAKEINSTANCE(19),0},
28   { &GUID_Key,    4, DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_MAKEINSTANCE(20),0},
29   { &GUID_Key,    5, DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_MAKEINSTANCE(21),0},
30   { &GUID_Key,    6, DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_MAKEINSTANCE(22),0},
31   { &GUID_Key,    7, DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_MAKEINSTANCE(23),0},
32   { &GUID_Key,    8, DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_MAKEINSTANCE(24),0},
33   { &GUID_Key,    9, DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_MAKEINSTANCE(25),0},
34   { &GUID_Key,   10, DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_MAKEINSTANCE(26),0},
35   { &GUID_Key,   11, DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_MAKEINSTANCE(27),0},
36   { &GUID_Key,   12, DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_MAKEINSTANCE(28),0},
37   { NULL,        13, DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_MAKEINSTANCE(5),0},
38 
39   { &GUID_Button,14, DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_MAKEINSTANCE(32),0}
40 };
41 
42 static const DIDATAFORMAT data_format = {
43     sizeof(DIDATAFORMAT),
44     sizeof(DIOBJECTDATAFORMAT),
45     DIDF_ABSAXIS,
46     32,
47     sizeof(obj_data_format) / sizeof(obj_data_format[0]),
48     (LPDIOBJECTDATAFORMAT)obj_data_format
49 };
50 
51 static BOOL CALLBACK enum_callback(const DIDEVICEOBJECTINSTANCEA *oi, void *info)
52 {
53     if (winetest_debug > 1)
54         trace(" Type:%4x Ofs:%3d Flags:%08x Name:%s\n",
55               oi->dwType, oi->dwOfs, oi->dwFlags, oi->tszName);
56     (*(int*)info)++;
57     return DIENUM_CONTINUE;
58 }
59 
60 static BOOL CALLBACK enum_type_callback(const DIDEVICEOBJECTINSTANCEA *oi, void *info)
61 {
62     DWORD expected = *(DWORD*)info;
63     ok (expected & DIDFT_GETTYPE(oi->dwType), "EnumObjects() enumerated wrong type for obj %s, expected: %08x got: %08x\n", oi->tszName, expected, oi->dwType);
64     return DIENUM_CONTINUE;
65 }
66 
67 static void test_object_info(IDirectInputDeviceA *device, HWND hwnd)
68 {
69     HRESULT hr;
70     DIPROPDWORD dp;
71     DIDEVICEOBJECTINSTANCEA obj_info;
72     DWORD obj_types[] = {DIDFT_BUTTON, DIDFT_AXIS, DIDFT_POV};
73     int type_index;
74     int cnt1 = 0;
75     DWORD cnt = 0;
76     DIDEVICEOBJECTDATA buffer[5];
77 
78     hr = IDirectInputDevice_EnumObjects(device, enum_callback, &cnt, DIDFT_ALL);
79     ok(SUCCEEDED(hr), "EnumObjects() failed: %08x\n", hr);
80 
81     hr = IDirectInputDevice_SetDataFormat(device, &data_format);
82     ok(SUCCEEDED(hr), "SetDataFormat() failed: %08x\n", hr);
83 
84     hr = IDirectInputDevice_EnumObjects(device, enum_callback, &cnt1, DIDFT_ALL);
85     ok(SUCCEEDED(hr), "EnumObjects() failed: %08x\n", hr);
86     if (0) /* fails for joystick only */
87     ok(cnt == cnt1, "Enum count changed from %d to %d\n", cnt, cnt1);
88 
89     /* Testing EnumObjects with different types of device objects */
90     for (type_index=0; type_index < sizeof(obj_types)/sizeof(obj_types[0]); type_index++)
91     {
92         hr = IDirectInputDevice_EnumObjects(device, enum_type_callback, &obj_types[type_index], obj_types[type_index]);
93         ok(SUCCEEDED(hr), "EnumObjects() failed: %08x\n", hr);
94     }
95 
96     /* Test buffered mode */
97     memset(&dp, 0, sizeof(dp));
98     dp.diph.dwSize = sizeof(DIPROPDWORD);
99     dp.diph.dwHeaderSize = sizeof(DIPROPHEADER);
100     dp.diph.dwHow = DIPH_DEVICE;
101     dp.diph.dwObj = 0;
102     dp.dwData = 0;
103 
104     hr = IDirectInputDevice_SetProperty(device, DIPROP_BUFFERSIZE, (LPCDIPROPHEADER)&dp.diph);
105     ok(hr == DI_OK, "SetProperty() failed: %08x\n", hr);
106     cnt = 5;
107     hr = IDirectInputDevice_GetDeviceData(device, sizeof(buffer[0]), buffer, &cnt, 0);
108     ok(hr == DI_OK && cnt == 5, "GetDeviceData() failed: %08x cnt: %d\n", hr, cnt);
109     hr = IDirectInputDevice_GetDeviceData(device, sizeof(DIDEVICEOBJECTDATA_DX3), buffer, &cnt, 0);
110     ok(hr == DIERR_NOTBUFFERED, "GetDeviceData() should have failed: %08x\n", hr);
111     IDirectInputDevice_Acquire(device);
112     hr = IDirectInputDevice_GetDeviceData(device, sizeof(DIDEVICEOBJECTDATA_DX3), buffer, &cnt, 0);
113     ok(hr == DIERR_NOTBUFFERED, "GetDeviceData() should have failed: %08x\n", hr);
114     IDirectInputDevice_Unacquire(device);
115 
116     dp.dwData = 20;
117     hr = IDirectInputDevice_SetProperty(device, DIPROP_BUFFERSIZE, (LPCDIPROPHEADER)&dp.diph);
118     ok(hr == DI_OK, "SetProperty() failed: %08x\n", hr);
119     cnt = 5;
120     hr = IDirectInputDevice_GetDeviceData(device, sizeof(buffer[0]), buffer, &cnt, 0);
121     ok(hr == DI_OK, "GetDeviceData() failed: %08x\n", hr);
122     hr = IDirectInputDevice_GetDeviceData(device, sizeof(DIDEVICEOBJECTDATA_DX3), buffer, &cnt, 0);
123     ok(hr == DIERR_NOTACQUIRED, "GetDeviceData() should have failed: %08x\n", hr);
124     hr = IDirectInputDevice_Acquire(device);
125     ok(hr == DI_OK, "Acquire() failed: %08x\n", hr);
126     cnt = 1;
127     hr = IDirectInputDevice_GetDeviceData(device, sizeof(buffer[0]), buffer, &cnt, 0);
128     ok(hr == DI_OK, "GetDeviceData() failed: %08x\n", hr);
129     hr = IDirectInputDevice_Unacquire(device);
130     ok(hr == DI_OK, "Unacquire() failed: %08x\n", hr);
131     cnt = 1;
132     hr = IDirectInputDevice_GetDeviceData(device, sizeof(buffer[0]), buffer, &cnt, 0);
133     ok(hr == DI_OK, "GetDeviceData() failed: %08x\n", hr);
134 
135     /* No need to test devices without axis */
136     obj_info.dwSize = sizeof(obj_info);
137     hr = IDirectInputDevice_GetObjectInfo(device, &obj_info, 16, DIPH_BYOFFSET);
138     if (SUCCEEDED(hr))
139     {
140         /* No device supports per axis relative/absolute mode */
141         dp.diph.dwHow = DIPH_BYOFFSET;
142         dp.diph.dwObj = 16;
143         dp.dwData = DIPROPAXISMODE_ABS;
144         hr = IDirectInputDevice_SetProperty(device, DIPROP_AXISMODE, &dp.diph);
145         ok(hr == DIERR_UNSUPPORTED, "SetProperty() returned: %08x\n", hr);
146         dp.diph.dwHow = DIPH_DEVICE;
147         hr = IDirectInputDevice_SetProperty(device, DIPROP_AXISMODE, &dp.diph);
148         ok(hr == DIERR_INVALIDPARAM, "SetProperty() returned: %08x\n", hr);
149         dp.diph.dwObj = 0;
150         hr = IDirectInputDevice_SetProperty(device, DIPROP_AXISMODE, &dp.diph);
151         ok(hr == DI_OK, "SetProperty() failed: %08x\n", hr);
152 
153         /* Cannot change mode while acquired */
154         hr = IDirectInputDevice_Acquire(device);
155         ok(hr == DI_OK, "Acquire() failed: %08x\n", hr);
156 
157         hr = IDirectInputDevice_SetProperty(device, DIPROP_AXISMODE, &dp.diph);
158         ok(hr == DIERR_ACQUIRED, "SetProperty() returned: %08x\n", hr);
159         hr = IDirectInputDevice_Unacquire(device);
160         ok(hr == DI_OK, "Unacquire() failed: %08x\n", hr);
161     }
162 }
163 
164 struct enum_data
165 {
166     IDirectInputA *pDI;
167     HWND hwnd;
168 };
169 
170 static BOOL CALLBACK enum_devices(const DIDEVICEINSTANCEA *lpddi, void *pvRef)
171 {
172     struct enum_data *data = pvRef;
173     IDirectInputDeviceA *device, *obj = NULL;
174     HRESULT hr;
175 
176     hr = IDirectInput_GetDeviceStatus(data->pDI, &lpddi->guidInstance);
177     ok(hr == DI_OK, "IDirectInput_GetDeviceStatus() failed: %08x\n", hr);
178 
179     if (hr == DI_OK)
180     {
181         hr = IDirectInput_CreateDevice(data->pDI, &lpddi->guidInstance, &device, NULL);
182         ok(SUCCEEDED(hr), "IDirectInput_CreateDevice() failed: %08x\n", hr);
183         trace("Testing device %p \"%s\"\n", device, lpddi->tszInstanceName);
184 
185         hr = IUnknown_QueryInterface(device, &IID_IDirectInputDevice2A, (LPVOID*)&obj);
186         ok(SUCCEEDED(hr), "IUnknown_QueryInterface(IID_IDirectInputDevice7A) failed: %08x\n", hr);
187         test_object_info(obj, data->hwnd);
188         if (obj) IUnknown_Release(obj);
189         obj = NULL;
190 
191         hr = IUnknown_QueryInterface(device, &IID_IDirectInputDevice2W, (LPVOID*)&obj);
192         ok(SUCCEEDED(hr), "IUnknown_QueryInterface(IID_IDirectInputDevice7W) failed: %08x\n", hr);
193         test_object_info(obj, data->hwnd);
194         if (obj) IUnknown_Release(obj);
195 
196         IUnknown_Release(device);
197     }
198     return DIENUM_CONTINUE;
199 }
200 
201 static void device_tests(void)
202 {
203     HRESULT hr;
204     IDirectInputA *pDI = NULL, *obj = NULL;
205     HINSTANCE hInstance = GetModuleHandleW(NULL);
206     HWND hwnd;
207     struct enum_data data;
208 
209     hr = CoCreateInstance(&CLSID_DirectInput, 0, 1, &IID_IDirectInput2A, (LPVOID*)&pDI);
210     if (hr == DIERR_OLDDIRECTINPUTVERSION || hr == DIERR_DEVICENOTREG)
211     {
212         skip("Tests require a newer dinput version\n");
213         return;
214     }
215     ok(SUCCEEDED(hr), "DirectInputCreateA() failed: %08x\n", hr);
216     if (FAILED(hr)) return;
217 
218     hr = IDirectInput_Initialize(pDI, hInstance, DIRECTINPUT_VERSION);
219     ok(SUCCEEDED(hr), "Initialize() failed: %08x\n", hr);
220     if (FAILED(hr)) return;
221 
222     hr = IUnknown_QueryInterface(pDI, &IID_IDirectInput2W, (LPVOID*)&obj);
223     ok(SUCCEEDED(hr), "QueryInterface(IDirectInput7W) failed: %08x\n", hr);
224 
225     hwnd = CreateWindowA("static", "Title", WS_OVERLAPPEDWINDOW, 10, 10, 200, 200, NULL, NULL,
226                          NULL, NULL);
227     ok(hwnd != NULL, "err: %d\n", GetLastError());
228     if (hwnd)
229     {
230         ShowWindow(hwnd, SW_SHOW);
231 
232         data.pDI = pDI;
233         data.hwnd = hwnd;
234         hr = IDirectInput_EnumDevices(pDI, 0, enum_devices, &data, DIEDFL_ALLDEVICES);
235         ok(SUCCEEDED(hr), "IDirectInput_EnumDevices() failed: %08x\n", hr);
236 
237 
238         /* If GetDeviceStatus returns DI_OK the device must exist */
239         hr = IDirectInput_GetDeviceStatus(pDI, &GUID_Joystick);
240         if (hr == DI_OK)
241         {
242             IDirectInputDeviceA *device = NULL;
243 
244             hr = IDirectInput_CreateDevice(pDI, &GUID_Joystick, &device, NULL);
245             ok(SUCCEEDED(hr), "IDirectInput_CreateDevice() failed: %08x\n", hr);
246             if (device) IUnknown_Release(device);
247         }
248 
249         DestroyWindow(hwnd);
250     }
251     if (obj) IUnknown_Release(obj);
252     if (pDI) IUnknown_Release(pDI);
253 }
254 
255 START_TEST(device)
256 {
257     CoInitialize(NULL);
258 
259     device_tests();
260 
261     CoUninitialize();
262 }
263