xref: /reactos/dll/directx/wine/dinput/joystick.c (revision c2c66aff)
1 /*  DirectInput Generic Joystick device
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998,1999 Lionel Ulmer
5  * Copyright 2000-2001 TransGaming Technologies Inc.
6  * Copyright 2009 Aric Stewart, CodeWeavers
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 /*
24  * To Do:
25  *	dead zone
26  *	force feedback
27  */
28 
29 #include "dinput_private.h"
30 
31 #include <stdio.h>
32 
33 #include "joystick_private.h"
34 
35 static inline JoystickGenericImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
36 {
37     return CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface), JoystickGenericImpl, base);
38 }
39 static inline JoystickGenericImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
40 {
41     return CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface), JoystickGenericImpl, base);
42 }
43 static inline IDirectInputDevice8A *IDirectInputDevice8A_from_impl(JoystickGenericImpl *This)
44 {
45     return &This->base.IDirectInputDevice8A_iface;
46 }
47 static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl(JoystickGenericImpl *This)
48 {
49     return &This->base.IDirectInputDevice8W_iface;
50 }
51 
52 DWORD typeFromGUID(REFGUID guid)
53 {
54     if (IsEqualGUID(guid, &GUID_ConstantForce)) {
55         return DIEFT_CONSTANTFORCE;
56     } else if (IsEqualGUID(guid, &GUID_Square)
57             || IsEqualGUID(guid, &GUID_Sine)
58             || IsEqualGUID(guid, &GUID_Triangle)
59             || IsEqualGUID(guid, &GUID_SawtoothUp)
60             || IsEqualGUID(guid, &GUID_SawtoothDown)) {
61         return DIEFT_PERIODIC;
62     } else if (IsEqualGUID(guid, &GUID_RampForce)) {
63         return DIEFT_RAMPFORCE;
64     } else if (IsEqualGUID(guid, &GUID_Spring)
65             || IsEqualGUID(guid, &GUID_Damper)
66             || IsEqualGUID(guid, &GUID_Inertia)
67             || IsEqualGUID(guid, &GUID_Friction)) {
68         return DIEFT_CONDITION;
69     } else if (IsEqualGUID(guid, &GUID_CustomForce)) {
70         return DIEFT_CUSTOMFORCE;
71     } else {
72         WARN("GUID (%s) is not a known force type\n", _dump_dinput_GUID(guid));
73         return 0;
74     }
75 }
76 
77 static void _dump_DIEFFECT_flags(DWORD dwFlags)
78 {
79     if (TRACE_ON(dinput)) {
80         unsigned int   i;
81         static const struct {
82             DWORD       mask;
83             const char  *name;
84         } flags[] = {
85 #define FE(x) { x, #x}
86             FE(DIEFF_CARTESIAN),
87             FE(DIEFF_OBJECTIDS),
88             FE(DIEFF_OBJECTOFFSETS),
89             FE(DIEFF_POLAR),
90             FE(DIEFF_SPHERICAL)
91 #undef FE
92         };
93         for (i = 0; i < (sizeof(flags) / sizeof(flags[0])); i++)
94             if (flags[i].mask & dwFlags)
95                 TRACE("%s ", flags[i].name);
96         TRACE("\n");
97     }
98 }
99 
100 static void _dump_DIENVELOPE(LPCDIENVELOPE env)
101 {
102     if (env->dwSize != sizeof(DIENVELOPE)) {
103         WARN("Non-standard DIENVELOPE structure size %d.\n", env->dwSize);
104     }
105     TRACE("Envelope has attack (level: %d time: %d), fade (level: %d time: %d)\n",
106           env->dwAttackLevel, env->dwAttackTime, env->dwFadeLevel, env->dwFadeTime);
107 }
108 
109 static void _dump_DICONSTANTFORCE(LPCDICONSTANTFORCE frc)
110 {
111     TRACE("Constant force has magnitude %d\n", frc->lMagnitude);
112 }
113 
114 static void _dump_DIPERIODIC(LPCDIPERIODIC frc)
115 {
116     TRACE("Periodic force has magnitude %d, offset %d, phase %d, period %d\n",
117           frc->dwMagnitude, frc->lOffset, frc->dwPhase, frc->dwPeriod);
118 }
119 
120 static void _dump_DIRAMPFORCE(LPCDIRAMPFORCE frc)
121 {
122     TRACE("Ramp force has start %d, end %d\n",
123           frc->lStart, frc->lEnd);
124 }
125 
126 static void _dump_DICONDITION(LPCDICONDITION frc)
127 {
128     TRACE("Condition has offset %d, pos/neg coefficients %d and %d, pos/neg saturations %d and %d, deadband %d\n",
129           frc->lOffset, frc->lPositiveCoefficient, frc->lNegativeCoefficient,
130           frc->dwPositiveSaturation, frc->dwNegativeSaturation, frc->lDeadBand);
131 }
132 
133 static void _dump_DICUSTOMFORCE(LPCDICUSTOMFORCE frc)
134 {
135     unsigned int i;
136     TRACE("Custom force uses %d channels, sample period %d.  Has %d samples at %p.\n",
137           frc->cChannels, frc->dwSamplePeriod, frc->cSamples, frc->rglForceData);
138     if (frc->cSamples % frc->cChannels != 0)
139         WARN("Custom force has a non-integral samples-per-channel count!\n");
140     if (TRACE_ON(dinput)) {
141         TRACE("Custom force data (time aligned, axes in order):\n");
142         for (i = 1; i <= frc->cSamples; ++i) {
143             TRACE("%d ", frc->rglForceData[i]);
144             if (i % frc->cChannels == 0)
145                 TRACE("\n");
146         }
147     }
148 }
149 
150 void dump_DIEFFECT(LPCDIEFFECT eff, REFGUID guid, DWORD dwFlags)
151 {
152     DWORD type = typeFromGUID(guid);
153     unsigned int i;
154 
155     TRACE("Dumping DIEFFECT structure:\n");
156     TRACE("  - dwSize: %d\n", eff->dwSize);
157     if ((eff->dwSize != sizeof(DIEFFECT)) && (eff->dwSize != sizeof(DIEFFECT_DX5))) {
158         WARN("Non-standard DIEFFECT structure size %d\n", eff->dwSize);
159     }
160     TRACE("  - dwFlags: %d\n", eff->dwFlags);
161     TRACE("    ");
162     _dump_DIEFFECT_flags(eff->dwFlags);
163     TRACE("  - dwDuration: %d\n", eff->dwDuration);
164     TRACE("  - dwGain: %d\n", eff->dwGain);
165 
166     if (eff->dwGain > 10000)
167         WARN("dwGain is out of range (>10,000)\n");
168 
169     TRACE("  - dwTriggerButton: %d\n", eff->dwTriggerButton);
170     TRACE("  - dwTriggerRepeatInterval: %d\n", eff->dwTriggerRepeatInterval);
171     TRACE("  - rglDirection: %p\n", eff->rglDirection);
172     TRACE("  - cbTypeSpecificParams: %d\n", eff->cbTypeSpecificParams);
173     TRACE("  - lpvTypeSpecificParams: %p\n", eff->lpvTypeSpecificParams);
174 
175     /* Only trace some members if dwFlags indicates they have data */
176     if (dwFlags & DIEP_AXES) {
177         TRACE("  - cAxes: %d\n", eff->cAxes);
178         TRACE("  - rgdwAxes: %p\n", eff->rgdwAxes);
179 
180         if (TRACE_ON(dinput) && eff->rgdwAxes) {
181             TRACE("    ");
182             for (i = 0; i < eff->cAxes; ++i)
183                 TRACE("%d ", eff->rgdwAxes[i]);
184             TRACE("\n");
185         }
186     }
187 
188     if (dwFlags & DIEP_ENVELOPE) {
189         TRACE("  - lpEnvelope: %p\n", eff->lpEnvelope);
190         if (eff->lpEnvelope != NULL)
191             _dump_DIENVELOPE(eff->lpEnvelope);
192     }
193 
194     if (eff->dwSize > sizeof(DIEFFECT_DX5))
195         TRACE("  - dwStartDelay: %d\n", eff->dwStartDelay);
196 
197     if (type == DIEFT_CONSTANTFORCE) {
198         if (eff->cbTypeSpecificParams != sizeof(DICONSTANTFORCE)) {
199             WARN("Effect claims to be a constant force but the type-specific params are the wrong size!\n");
200         } else {
201             _dump_DICONSTANTFORCE(eff->lpvTypeSpecificParams);
202         }
203     } else if (type == DIEFT_PERIODIC) {
204         if (eff->cbTypeSpecificParams != sizeof(DIPERIODIC)) {
205             WARN("Effect claims to be a periodic force but the type-specific params are the wrong size!\n");
206         } else {
207             _dump_DIPERIODIC(eff->lpvTypeSpecificParams);
208         }
209     } else if (type == DIEFT_RAMPFORCE) {
210         if (eff->cbTypeSpecificParams != sizeof(DIRAMPFORCE)) {
211             WARN("Effect claims to be a ramp force but the type-specific params are the wrong size!\n");
212         } else {
213             _dump_DIRAMPFORCE(eff->lpvTypeSpecificParams);
214         }
215     } else if (type == DIEFT_CONDITION) {
216         if (eff->cbTypeSpecificParams == sizeof(DICONDITION)) {
217             _dump_DICONDITION(eff->lpvTypeSpecificParams);
218         } else if (eff->cbTypeSpecificParams == 2 * sizeof(DICONDITION)) {
219             DICONDITION *condition = eff->lpvTypeSpecificParams;
220             _dump_DICONDITION(&condition[0]);
221             _dump_DICONDITION(&condition[1]);
222         } else {
223             WARN("Effect claims to be a condition but the type-specific params are the wrong size!\n");
224         }
225     } else if (type == DIEFT_CUSTOMFORCE) {
226         if (eff->cbTypeSpecificParams != sizeof(DICUSTOMFORCE)) {
227             WARN("Effect claims to be a custom force but the type-specific params are the wrong size!\n");
228         } else {
229             _dump_DICUSTOMFORCE(eff->lpvTypeSpecificParams);
230         }
231     }
232 }
233 
234 BOOL device_disabled_registry(const char* name)
235 {
236     static const char disabled_str[] = "disabled";
237     static const char joystick_key[] = "Joysticks";
238     char buffer[MAX_PATH];
239     HKEY hkey, appkey, temp;
240     BOOL do_disable = FALSE;
241 
242     get_app_key(&hkey, &appkey);
243 
244     /* Joystick settings are in the 'joysticks' subkey */
245     if (appkey)
246     {
247         if (RegOpenKeyA(appkey, joystick_key, &temp)) temp = 0;
248         RegCloseKey(appkey);
249         appkey = temp;
250     }
251     if (hkey)
252     {
253         if (RegOpenKeyA(hkey, joystick_key, &temp)) temp = 0;
254         RegCloseKey(hkey);
255         hkey = temp;
256     }
257 
258     /* Look for the "controllername"="disabled" key */
259     if (!get_config_key(hkey, appkey, name, buffer, sizeof(buffer)))
260         if (!strcmp(disabled_str, buffer))
261         {
262             TRACE("Disabling joystick '%s' based on registry key.\n", name);
263             do_disable = TRUE;
264         }
265 
266     if (appkey) RegCloseKey(appkey);
267     if (hkey)   RegCloseKey(hkey);
268 
269     return do_disable;
270 }
271 
272 /******************************************************************************
273   *     SetProperty : change input device properties
274   */
275 HRESULT WINAPI JoystickWGenericImpl_SetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPCDIPROPHEADER ph)
276 {
277     JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
278     DWORD i;
279     ObjProps remap_props;
280 
281     TRACE("(%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
282 
283     if (ph == NULL) {
284         WARN("invalid parameter: ph == NULL\n");
285         return DIERR_INVALIDPARAM;
286     }
287 
288     if (TRACE_ON(dinput))
289         _dump_DIPROPHEADER(ph);
290 
291     if (IS_DIPROP(rguid)) {
292         switch (LOWORD(rguid)) {
293         case (DWORD_PTR)DIPROP_RANGE: {
294             LPCDIPROPRANGE pr = (LPCDIPROPRANGE)ph;
295             if (ph->dwHow == DIPH_DEVICE) {
296 
297                 /* Many games poll the joystick immediately after setting the range
298                  * for calibration purposes, so the old values need to be remapped
299                  * to the new range before it does so */
300 
301                 TRACE("proprange(%d,%d) all\n", pr->lMin, pr->lMax);
302                 for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++) {
303 
304                     remap_props.lDevMin = This->props[i].lMin;
305                     remap_props.lDevMax = This->props[i].lMax;
306 
307                     remap_props.lDeadZone = This->props[i].lDeadZone;
308                     remap_props.lSaturation = This->props[i].lSaturation;
309 
310                     remap_props.lMin = pr->lMin;
311                     remap_props.lMax = pr->lMax;
312 
313                     switch (This->base.data_format.wine_df->rgodf[i].dwOfs) {
314                     case DIJOFS_X        : This->js.lX  = joystick_map_axis(&remap_props, This->js.lX); break;
315                     case DIJOFS_Y        : This->js.lY  = joystick_map_axis(&remap_props, This->js.lY); break;
316                     case DIJOFS_Z        : This->js.lZ  = joystick_map_axis(&remap_props, This->js.lZ); break;
317                     case DIJOFS_RX       : This->js.lRx = joystick_map_axis(&remap_props, This->js.lRx); break;
318                     case DIJOFS_RY       : This->js.lRy = joystick_map_axis(&remap_props, This->js.lRy); break;
319                     case DIJOFS_RZ       : This->js.lRz = joystick_map_axis(&remap_props, This->js.lRz); break;
320                     case DIJOFS_SLIDER(0): This->js.rglSlider[0] = joystick_map_axis(&remap_props, This->js.rglSlider[0]); break;
321                     case DIJOFS_SLIDER(1): This->js.rglSlider[1] = joystick_map_axis(&remap_props, This->js.rglSlider[1]); break;
322 	            default: break;
323                     }
324 
325                     This->props[i].lMin = pr->lMin;
326                     This->props[i].lMax = pr->lMax;
327                 }
328             } else {
329                 int obj = find_property(&This->base.data_format, ph);
330 
331                 TRACE("proprange(%d,%d) obj=%d\n", pr->lMin, pr->lMax, obj);
332                 if (obj >= 0) {
333 
334                     remap_props.lDevMin = This->props[obj].lMin;
335                     remap_props.lDevMax = This->props[obj].lMax;
336 
337                     remap_props.lDeadZone = This->props[obj].lDeadZone;
338                     remap_props.lSaturation = This->props[obj].lSaturation;
339 
340                     remap_props.lMin = pr->lMin;
341                     remap_props.lMax = pr->lMax;
342 
343                     switch (This->base.data_format.wine_df->rgodf[obj].dwOfs) {
344                     case DIJOFS_X        : This->js.lX  = joystick_map_axis(&remap_props, This->js.lX); break;
345                     case DIJOFS_Y        : This->js.lY  = joystick_map_axis(&remap_props, This->js.lY); break;
346                     case DIJOFS_Z        : This->js.lZ  = joystick_map_axis(&remap_props, This->js.lZ); break;
347                     case DIJOFS_RX       : This->js.lRx = joystick_map_axis(&remap_props, This->js.lRx); break;
348                     case DIJOFS_RY       : This->js.lRy = joystick_map_axis(&remap_props, This->js.lRy); break;
349                     case DIJOFS_RZ       : This->js.lRz = joystick_map_axis(&remap_props, This->js.lRz); break;
350                     case DIJOFS_SLIDER(0): This->js.rglSlider[0] = joystick_map_axis(&remap_props, This->js.rglSlider[0]); break;
351                     case DIJOFS_SLIDER(1): This->js.rglSlider[1] = joystick_map_axis(&remap_props, This->js.rglSlider[1]); break;
352 		    default: break;
353                     }
354 
355                     This->props[obj].lMin = pr->lMin;
356                     This->props[obj].lMax = pr->lMax;
357                     return DI_OK;
358                 }
359             }
360             break;
361         }
362         case (DWORD_PTR)DIPROP_DEADZONE: {
363             LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
364             if (ph->dwHow == DIPH_DEVICE) {
365                 TRACE("deadzone(%d) all\n", pd->dwData);
366                 for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++)
367                     This->props[i].lDeadZone  = pd->dwData;
368             } else {
369                 int obj = find_property(&This->base.data_format, ph);
370 
371                 TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
372                 if (obj >= 0) {
373                     This->props[obj].lDeadZone  = pd->dwData;
374                     return DI_OK;
375                 }
376             }
377             break;
378         }
379         case (DWORD_PTR)DIPROP_SATURATION: {
380             LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
381             if (ph->dwHow == DIPH_DEVICE) {
382                 TRACE("saturation(%d) all\n", pd->dwData);
383                 for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++)
384                     This->props[i].lSaturation = pd->dwData;
385             } else {
386                 int obj = find_property(&This->base.data_format, ph);
387 
388                 TRACE("saturation(%d) obj=%d\n", pd->dwData, obj);
389                 if (obj >= 0) {
390                     This->props[obj].lSaturation = pd->dwData;
391                     return DI_OK;
392                 }
393             }
394             break;
395         }
396         default:
397             return IDirectInputDevice2WImpl_SetProperty(iface, rguid, ph);
398         }
399     }
400 
401     return DI_OK;
402 }
403 
404 HRESULT WINAPI JoystickAGenericImpl_SetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPCDIPROPHEADER ph)
405 {
406     JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface);
407     return JoystickWGenericImpl_SetProperty(IDirectInputDevice8W_from_impl(This), rguid, ph);
408 }
409 
410 #define DEBUG_TYPE(x) case (x): str = #x; break
411 void _dump_DIDEVCAPS(const DIDEVCAPS *lpDIDevCaps)
412 {
413     int type = GET_DIDEVICE_TYPE(lpDIDevCaps->dwDevType);
414     const char *str, *hid = "";
415     TRACE("dwSize: %d\n", lpDIDevCaps->dwSize);
416     TRACE("dwFlags: %08x\n", lpDIDevCaps->dwFlags);
417     switch(type)
418     {
419         /* Direct X <= 7 definitions */
420         DEBUG_TYPE(DIDEVTYPE_DEVICE);
421         DEBUG_TYPE(DIDEVTYPE_MOUSE);
422         DEBUG_TYPE(DIDEVTYPE_KEYBOARD);
423         DEBUG_TYPE(DIDEVTYPE_JOYSTICK);
424         /* Direct X >= 8 definitions */
425         DEBUG_TYPE(DI8DEVTYPE_DEVICE);
426         DEBUG_TYPE(DI8DEVTYPE_MOUSE);
427         DEBUG_TYPE(DI8DEVTYPE_KEYBOARD);
428         DEBUG_TYPE(DI8DEVTYPE_JOYSTICK);
429         DEBUG_TYPE(DI8DEVTYPE_GAMEPAD);
430         DEBUG_TYPE(DI8DEVTYPE_DRIVING);
431         DEBUG_TYPE(DI8DEVTYPE_FLIGHT);
432         DEBUG_TYPE(DI8DEVTYPE_1STPERSON);
433         DEBUG_TYPE(DI8DEVTYPE_DEVICECTRL);
434         DEBUG_TYPE(DI8DEVTYPE_SCREENPOINTER);
435         DEBUG_TYPE(DI8DEVTYPE_REMOTE);
436         DEBUG_TYPE(DI8DEVTYPE_SUPPLEMENTAL);
437         default: str = "UNKNOWN";
438     }
439 
440     if (lpDIDevCaps->dwDevType & DIDEVTYPE_HID)
441         hid = " (HID)";
442 
443     TRACE("dwDevType: %08x %s%s\n", lpDIDevCaps->dwDevType, str, hid);
444     TRACE("dwAxes: %d\n", lpDIDevCaps->dwAxes);
445     TRACE("dwButtons: %d\n", lpDIDevCaps->dwButtons);
446     TRACE("dwPOVs: %d\n", lpDIDevCaps->dwPOVs);
447     if (lpDIDevCaps->dwSize > sizeof(DIDEVCAPS_DX3)) {
448         TRACE("dwFFSamplePeriod: %d\n", lpDIDevCaps->dwFFSamplePeriod);
449         TRACE("dwFFMinTimeResolution: %d\n", lpDIDevCaps->dwFFMinTimeResolution);
450         TRACE("dwFirmwareRevision: %d\n", lpDIDevCaps->dwFirmwareRevision);
451         TRACE("dwHardwareRevision: %d\n", lpDIDevCaps->dwHardwareRevision);
452         TRACE("dwFFDriverVersion: %d\n", lpDIDevCaps->dwFFDriverVersion);
453     }
454 }
455 #undef DEBUG_TYPE
456 
457 HRESULT WINAPI JoystickWGenericImpl_GetCapabilities(LPDIRECTINPUTDEVICE8W iface, LPDIDEVCAPS lpDIDevCaps)
458 {
459     JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
460     int size;
461 
462     TRACE("%p->(%p)\n",iface,lpDIDevCaps);
463 
464     if (lpDIDevCaps == NULL) {
465         WARN("invalid pointer\n");
466         return E_POINTER;
467     }
468 
469     size = lpDIDevCaps->dwSize;
470 
471     if (!(size == sizeof(DIDEVCAPS) || size == sizeof(DIDEVCAPS_DX3))) {
472         WARN("invalid parameter\n");
473         return DIERR_INVALIDPARAM;
474     }
475 
476     CopyMemory(lpDIDevCaps, &This->devcaps, size);
477     lpDIDevCaps->dwSize = size;
478 
479     if (TRACE_ON(dinput))
480         _dump_DIDEVCAPS(lpDIDevCaps);
481 
482     return DI_OK;
483 }
484 
485 HRESULT WINAPI JoystickAGenericImpl_GetCapabilities(LPDIRECTINPUTDEVICE8A iface, LPDIDEVCAPS lpDIDevCaps)
486 {
487     JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface);
488     return JoystickWGenericImpl_GetCapabilities(IDirectInputDevice8W_from_impl(This), lpDIDevCaps);
489 }
490 
491 /******************************************************************************
492   *     GetObjectInfo : get object info
493   */
494 HRESULT WINAPI JoystickWGenericImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8W iface,
495         LPDIDEVICEOBJECTINSTANCEW pdidoi, DWORD dwObj, DWORD dwHow)
496 {
497     static const WCHAR axisW[] = {'A','x','i','s',' ','%','d',0};
498     static const WCHAR povW[] = {'P','O','V',' ','%','d',0};
499     static const WCHAR buttonW[] = {'B','u','t','t','o','n',' ','%','d',0};
500     HRESULT res;
501 
502     res = IDirectInputDevice2WImpl_GetObjectInfo(iface, pdidoi, dwObj, dwHow);
503     if (res != DI_OK) return res;
504 
505     if (pdidoi->dwType & DIDFT_AXIS) {
506         sprintfW(pdidoi->tszName, axisW, DIDFT_GETINSTANCE(pdidoi->dwType));
507         pdidoi->dwFlags |= DIDOI_ASPECTPOSITION;
508     } else if (pdidoi->dwType & DIDFT_POV)
509         sprintfW(pdidoi->tszName, povW, DIDFT_GETINSTANCE(pdidoi->dwType));
510     else if (pdidoi->dwType & DIDFT_BUTTON)
511         sprintfW(pdidoi->tszName, buttonW, DIDFT_GETINSTANCE(pdidoi->dwType));
512 
513     _dump_OBJECTINSTANCEW(pdidoi);
514     return res;
515 }
516 
517 HRESULT WINAPI JoystickAGenericImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8A iface,
518         LPDIDEVICEOBJECTINSTANCEA pdidoi, DWORD dwObj, DWORD dwHow)
519 {
520     JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface);
521     HRESULT res;
522     DIDEVICEOBJECTINSTANCEW didoiW;
523     DWORD dwSize = pdidoi->dwSize;
524 
525     didoiW.dwSize = sizeof(didoiW);
526     res = JoystickWGenericImpl_GetObjectInfo(IDirectInputDevice8W_from_impl(This), &didoiW, dwObj, dwHow);
527     if (res != DI_OK) return res;
528 
529     memset(pdidoi, 0, pdidoi->dwSize);
530     memcpy(pdidoi, &didoiW, FIELD_OFFSET(DIDEVICEOBJECTINSTANCEW, tszName));
531     pdidoi->dwSize = dwSize;
532     WideCharToMultiByte(CP_ACP, 0, didoiW.tszName, -1, pdidoi->tszName,
533                         sizeof(pdidoi->tszName), NULL, NULL);
534 
535     return res;
536 }
537 
538 /******************************************************************************
539   *     GetProperty : get input device properties
540   */
541 HRESULT WINAPI JoystickWGenericImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph)
542 {
543     JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
544 
545     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(rguid), pdiph);
546 
547     if (TRACE_ON(dinput))
548         _dump_DIPROPHEADER(pdiph);
549 
550     if (IS_DIPROP(rguid)) {
551         switch (LOWORD(rguid)) {
552         case (DWORD_PTR) DIPROP_RANGE: {
553             LPDIPROPRANGE pr = (LPDIPROPRANGE)pdiph;
554             int obj = find_property(&This->base.data_format, pdiph);
555 
556             /* The app is querying the current range of the axis
557              * return the lMin and lMax values */
558             if (obj >= 0) {
559                 pr->lMin = This->props[obj].lMin;
560                 pr->lMax = This->props[obj].lMax;
561                 TRACE("range(%d, %d) obj=%d\n", pr->lMin, pr->lMax, obj);
562                 return DI_OK;
563             }
564             break;
565         }
566         case (DWORD_PTR) DIPROP_DEADZONE: {
567             LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
568             int obj = find_property(&This->base.data_format, pdiph);
569 
570             if (obj >= 0) {
571                 pd->dwData = This->props[obj].lDeadZone;
572                 TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
573                 return DI_OK;
574             }
575             break;
576         }
577         case (DWORD_PTR) DIPROP_SATURATION: {
578             LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
579             int obj = find_property(&This->base.data_format, pdiph);
580 
581             if (obj >= 0) {
582                 pd->dwData = This->props[obj].lSaturation;
583                 TRACE("saturation(%d) obj=%d\n", pd->dwData, obj);
584                 return DI_OK;
585             }
586             break;
587         }
588         case (DWORD_PTR) DIPROP_PRODUCTNAME:
589         case (DWORD_PTR) DIPROP_INSTANCENAME: {
590             DIPROPSTRING *ps = (DIPROPSTRING*) pdiph;
591             DIDEVICEINSTANCEW didev;
592 
593             didev.dwSize = sizeof(didev);
594 
595             IDirectInputDevice_GetDeviceInfo(iface, &didev);
596             if (LOWORD(rguid) == (DWORD_PTR) DIPROP_PRODUCTNAME)
597                 lstrcpynW(ps->wsz, didev.tszProductName, MAX_PATH);
598             else
599                 lstrcpynW(ps->wsz, didev.tszInstanceName, MAX_PATH);
600 
601             return DI_OK;
602         }
603         default:
604             return IDirectInputDevice2WImpl_GetProperty(iface, rguid, pdiph);
605         }
606     }
607 
608     return DI_OK;
609 }
610 
611 HRESULT WINAPI JoystickAGenericImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPDIPROPHEADER pdiph)
612 {
613     JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface);
614     return JoystickWGenericImpl_GetProperty(IDirectInputDevice8W_from_impl(This), rguid, pdiph);
615 }
616 
617 /******************************************************************************
618   *     GetDeviceInfo : get information about a device's identity
619   */
620 HRESULT WINAPI JoystickAGenericImpl_GetDeviceInfo(
621     LPDIRECTINPUTDEVICE8A iface,
622     LPDIDEVICEINSTANCEA pdidi)
623 {
624     JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface);
625     DIPROPDWORD pd;
626     DWORD index = 0;
627 
628     TRACE("(%p,%p)\n", iface, pdidi);
629 
630     if (pdidi == NULL) {
631         WARN("invalid pointer\n");
632         return E_POINTER;
633     }
634 
635     if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3A)) &&
636         (pdidi->dwSize != sizeof(DIDEVICEINSTANCEA))) {
637         WARN("invalid parameter: pdidi->dwSize = %d\n", pdidi->dwSize);
638         return DIERR_INVALIDPARAM;
639     }
640 
641     /* Try to get joystick index */
642     pd.diph.dwSize = sizeof(pd);
643     pd.diph.dwHeaderSize = sizeof(pd.diph);
644     pd.diph.dwObj = 0;
645     pd.diph.dwHow = DIPH_DEVICE;
646     if (SUCCEEDED(IDirectInputDevice2_GetProperty(iface, DIPROP_JOYSTICKID, &pd.diph)))
647         index = pd.dwData;
648 
649     /* Return joystick */
650     pdidi->guidInstance = This->guidInstance;
651     pdidi->guidProduct = This->guidProduct;
652     /* we only support traditional joysticks for now */
653     pdidi->dwDevType = This->devcaps.dwDevType;
654     snprintf(pdidi->tszInstanceName, MAX_PATH, "Joystick %d", index);
655     strcpy(pdidi->tszProductName, This->name);
656     if (pdidi->dwSize > sizeof(DIDEVICEINSTANCE_DX3A)) {
657         pdidi->guidFFDriver = GUID_NULL;
658         pdidi->wUsagePage = 0;
659         pdidi->wUsage = 0;
660     }
661 
662     return DI_OK;
663 }
664 
665 /******************************************************************************
666   *     GetDeviceInfo : get information about a device's identity
667   */
668 HRESULT WINAPI JoystickWGenericImpl_GetDeviceInfo(
669     LPDIRECTINPUTDEVICE8W iface,
670     LPDIDEVICEINSTANCEW pdidi)
671 {
672     JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
673     CHAR buffer[MAX_PATH];
674     DIPROPDWORD pd;
675     DWORD index = 0;
676 
677     TRACE("(%p,%p)\n", iface, pdidi);
678 
679     if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3W)) &&
680         (pdidi->dwSize != sizeof(DIDEVICEINSTANCEW))) {
681         WARN("invalid parameter: pdidi->dwSize = %d\n", pdidi->dwSize);
682         return DIERR_INVALIDPARAM;
683     }
684 
685     /* Try to get joystick index */
686     pd.diph.dwSize = sizeof(pd);
687     pd.diph.dwHeaderSize = sizeof(pd.diph);
688     pd.diph.dwObj = 0;
689     pd.diph.dwHow = DIPH_DEVICE;
690     if (SUCCEEDED(IDirectInputDevice2_GetProperty(iface, DIPROP_JOYSTICKID, &pd.diph)))
691         index = pd.dwData;
692 
693     /* Return joystick */
694     pdidi->guidInstance = This->guidInstance;
695     pdidi->guidProduct = This->guidProduct;
696     /* we only support traditional joysticks for now */
697     pdidi->dwDevType = This->devcaps.dwDevType;
698     snprintf(buffer, sizeof(buffer), "Joystick %d", index);
699     MultiByteToWideChar(CP_ACP, 0, buffer, -1, pdidi->tszInstanceName, MAX_PATH);
700     MultiByteToWideChar(CP_ACP, 0, This->name, -1, pdidi->tszProductName, MAX_PATH);
701     if (pdidi->dwSize > sizeof(DIDEVICEINSTANCE_DX3W)) {
702         pdidi->guidFFDriver = GUID_NULL;
703         pdidi->wUsagePage = 0;
704         pdidi->wUsage = 0;
705     }
706 
707     return DI_OK;
708 }
709 
710 HRESULT WINAPI JoystickWGenericImpl_Poll(LPDIRECTINPUTDEVICE8W iface)
711 {
712     JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
713 
714     TRACE("(%p)\n",This);
715 
716     if (!This->base.acquired) {
717         WARN("not acquired\n");
718         return DIERR_NOTACQUIRED;
719     }
720 
721     This->joy_polldev(IDirectInputDevice8A_from_impl(This));
722     return DI_OK;
723 }
724 
725 HRESULT WINAPI JoystickAGenericImpl_Poll(LPDIRECTINPUTDEVICE8A iface)
726 {
727     JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface);
728     return JoystickWGenericImpl_Poll(IDirectInputDevice8W_from_impl(This));
729 }
730 
731 /******************************************************************************
732   *     GetDeviceState : returns the "state" of the joystick.
733   *
734   */
735 HRESULT WINAPI JoystickWGenericImpl_GetDeviceState(LPDIRECTINPUTDEVICE8W iface, DWORD len, LPVOID ptr)
736 {
737     JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
738 
739     TRACE("(%p,0x%08x,%p)\n", This, len, ptr);
740 
741     if (!This->base.acquired) {
742         WARN("not acquired\n");
743         return DIERR_NOTACQUIRED;
744     }
745 
746     /* update joystick state */
747     This->joy_polldev(IDirectInputDevice8A_from_impl(This));
748 
749     /* convert and copy data to user supplied buffer */
750     fill_DataFormat(ptr, len, &This->js, &This->base.data_format);
751 
752     return DI_OK;
753 }
754 
755 HRESULT WINAPI JoystickAGenericImpl_GetDeviceState(LPDIRECTINPUTDEVICE8A iface, DWORD len, LPVOID ptr)
756 {
757     JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface);
758     return JoystickWGenericImpl_GetDeviceState(IDirectInputDevice8W_from_impl(This), len, ptr);
759 }
760 
761 
762 HRESULT WINAPI JoystickWGenericImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W iface,
763                                                    LPDIACTIONFORMATW lpdiaf,
764                                                    LPCWSTR lpszUserName,
765                                                    DWORD dwFlags)
766 {
767     JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
768     unsigned int i, j;
769     BOOL has_actions = FALSE;
770     DWORD object_types[] = { DIDFT_AXIS, DIDFT_BUTTON };
771     DWORD type_map[] = { DIDFT_RELAXIS, DIDFT_PSHBUTTON };
772 
773     FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
774 
775     for (i=0; i < lpdiaf->dwNumActions; i++)
776     {
777         DWORD inst = (0x000000ff & (lpdiaf->rgoAction[i].dwSemantic)) - 1;
778         DWORD type = 0x000000ff & (lpdiaf->rgoAction[i].dwSemantic >> 8);
779         DWORD genre = 0xff000000 & lpdiaf->rgoAction[i].dwSemantic;
780 
781         /* Don't touch a user configured action */
782         if (lpdiaf->rgoAction[i].dwHow == DIAH_USERCONFIG) continue;
783 
784         /* Only consider actions of the right genre */
785         if (lpdiaf->dwGenre != genre && genre != DIGENRE_ANY) continue;
786 
787         for (j=0; j < sizeof(object_types)/sizeof(object_types[0]); j++)
788         {
789             if (type & object_types[j])
790             {
791                 /* Ensure that the object exists */
792                 LPDIOBJECTDATAFORMAT odf = dataformat_to_odf_by_type(This->base.data_format.wine_df, inst, object_types[j]);
793 
794                 if (odf != NULL)
795                 {
796                     lpdiaf->rgoAction[i].dwObjID = type_map[j] | (0x0000ff00 & (inst << 8));
797                     lpdiaf->rgoAction[i].guidInstance = This->base.guid;
798                     lpdiaf->rgoAction[i].dwHow = DIAH_DEFAULT;
799 
800                     has_actions = TRUE;
801 
802                     /* No need to try other types if the action was already mapped */
803                     break;
804                 }
805             }
806         }
807     }
808 
809     if (!has_actions) return DI_NOEFFECT;
810 
811     return IDirectInputDevice8WImpl_BuildActionMap(iface, lpdiaf, lpszUserName, dwFlags);
812 }
813 
814 HRESULT WINAPI JoystickAGenericImpl_BuildActionMap(LPDIRECTINPUTDEVICE8A iface,
815                                                    LPDIACTIONFORMATA lpdiaf,
816                                                    LPCSTR lpszUserName,
817                                                    DWORD dwFlags)
818 {
819     JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface);
820     DIACTIONFORMATW diafW;
821     HRESULT hr;
822     WCHAR *lpszUserNameW = NULL;
823     int username_size;
824 
825     diafW.rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*lpdiaf->dwNumActions);
826     _copy_diactionformatAtoW(&diafW, lpdiaf);
827 
828     if (lpszUserName != NULL)
829     {
830         username_size = MultiByteToWideChar(CP_ACP, 0, lpszUserName, -1, NULL, 0);
831         lpszUserNameW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*username_size);
832         MultiByteToWideChar(CP_ACP, 0, lpszUserName, -1, lpszUserNameW, username_size);
833     }
834 
835     hr = JoystickWGenericImpl_BuildActionMap(&This->base.IDirectInputDevice8W_iface, &diafW, lpszUserNameW, dwFlags);
836 
837     _copy_diactionformatWtoA(lpdiaf, &diafW);
838     HeapFree(GetProcessHeap(), 0, diafW.rgoAction);
839     HeapFree(GetProcessHeap(), 0, lpszUserNameW);
840 
841     return hr;
842 }
843 
844 HRESULT WINAPI JoystickWGenericImpl_SetActionMap(LPDIRECTINPUTDEVICE8W iface,
845                                                  LPDIACTIONFORMATW lpdiaf,
846                                                  LPCWSTR lpszUserName,
847                                                  DWORD dwFlags)
848 {
849     JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface);
850 
851     FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
852 
853     return _set_action_map(iface, lpdiaf, lpszUserName, dwFlags, This->base.data_format.wine_df);
854 }
855 
856 HRESULT WINAPI JoystickAGenericImpl_SetActionMap(LPDIRECTINPUTDEVICE8A iface,
857                                                  LPDIACTIONFORMATA lpdiaf,
858                                                  LPCSTR lpszUserName,
859                                                  DWORD dwFlags)
860 {
861     JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface);
862     DIACTIONFORMATW diafW;
863     HRESULT hr;
864     WCHAR *lpszUserNameW = NULL;
865     int username_size;
866 
867     diafW.rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*lpdiaf->dwNumActions);
868     _copy_diactionformatAtoW(&diafW, lpdiaf);
869 
870     if (lpszUserName != NULL)
871     {
872         username_size = MultiByteToWideChar(CP_ACP, 0, lpszUserName, -1, NULL, 0);
873         lpszUserNameW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*username_size);
874         MultiByteToWideChar(CP_ACP, 0, lpszUserName, -1, lpszUserNameW, username_size);
875     }
876 
877     hr = JoystickWGenericImpl_SetActionMap(&This->base.IDirectInputDevice8W_iface, &diafW, lpszUserNameW, dwFlags);
878 
879     HeapFree(GetProcessHeap(), 0, diafW.rgoAction);
880     HeapFree(GetProcessHeap(), 0, lpszUserNameW);
881 
882     return hr;
883 }
884 
885 /*
886  * This maps the read value (from the input event) to a value in the
887  * 'wanted' range.
888  * Notes:
889  *   Dead zone is in % multiplied by a 100 (range 0..10000)
890  */
891 LONG joystick_map_axis(ObjProps *props, int val)
892 {
893     LONG ret;
894     LONG dead_zone = MulDiv( props->lDeadZone, props->lDevMax - props->lDevMin, 10000 );
895     LONG dev_range = props->lDevMax - props->lDevMin - dead_zone;
896 
897     /* Center input */
898     val -= (props->lDevMin + props->lDevMax) / 2;
899 
900     /* Remove dead zone */
901     if (abs( val ) <= dead_zone / 2)
902         val = 0;
903     else
904         val = val < 0 ? val + dead_zone / 2 : val - dead_zone / 2;
905 
906     /* Scale and map the value from the device range into the required range */
907     ret = MulDiv( val, props->lMax - props->lMin, dev_range ) +
908           (props->lMin + props->lMax) / 2;
909 
910     /* Clamp in case or rounding errors */
911     if      (ret > props->lMax) ret = props->lMax;
912     else if (ret < props->lMin) ret = props->lMin;
913 
914     TRACE( "(%d <%d> %d) -> (%d <%d> %d): val=%d ret=%d\n",
915            props->lDevMin, dead_zone, props->lDevMax,
916            props->lMin, props->lDeadZone, props->lMax,
917            val, ret );
918 
919     return ret;
920 }
921 
922 /*
923  * Maps POV x & y event values to a DX "clock" position:
924  *         0
925  *   31500    4500
926  * 27000  -1    9000
927  *   22500   13500
928  *       18000
929  */
930 DWORD joystick_map_pov(const POINTL *p)
931 {
932     if (p->x > 0)
933         return p->y < 0 ?  4500 : !p->y ?  9000 : 13500;
934     else if (p->x < 0)
935         return p->y < 0 ? 31500 : !p->y ? 27000 : 22500;
936     else
937         return p->y < 0 ?     0 : !p->y ?    -1 : 18000;
938 }
939 
940 /*
941  * Setup the dinput options.
942  */
943 
944 HRESULT setup_dinput_options(JoystickGenericImpl *This, const int *default_axis_map)
945 {
946     char buffer[MAX_PATH+16];
947     HKEY hkey, appkey;
948     int tokens = 0;
949     int axis = 0;
950     int pov = 0;
951 
952     get_app_key(&hkey, &appkey);
953 
954     /* get options */
955 
956     if (!get_config_key(hkey, appkey, "DefaultDeadZone", buffer, sizeof(buffer)))
957     {
958         This->deadzone = atoi(buffer);
959         TRACE("setting default deadzone to: \"%s\" %d\n", buffer, This->deadzone);
960     }
961 
962     This->axis_map = HeapAlloc(GetProcessHeap(), 0, This->device_axis_count * sizeof(int));
963     if (!This->axis_map) return DIERR_OUTOFMEMORY;
964 
965     if (!get_config_key(hkey, appkey, This->name, buffer, sizeof(buffer)))
966     {
967         static const char *axis_names[] = {"X", "Y", "Z", "Rx", "Ry", "Rz",
968                                            "Slider1", "Slider2",
969                                            "POV1", "POV2", "POV3", "POV4"};
970         const char *delim = ",";
971         char * ptr;
972         TRACE("\"%s\" = \"%s\"\n", This->name, buffer);
973 
974         if ((ptr = strtok(buffer, delim)) != NULL)
975         {
976             do
977             {
978                 int i;
979 
980                 for (i = 0; i < sizeof(axis_names) / sizeof(axis_names[0]); i++)
981                 {
982                     if (!strcmp(ptr, axis_names[i]))
983                     {
984                         if (!strncmp(ptr, "POV", 3))
985                         {
986                             if (pov >= 4)
987                             {
988                                 WARN("Only 4 POVs supported - ignoring extra\n");
989                                 i = -1;
990                             }
991                             else
992                             {
993                                 /* Pov takes two axes */
994                                 This->axis_map[tokens++] = i;
995                                 pov++;
996                             }
997                         }
998                         else
999                         {
1000                             if (axis >= 8)
1001                             {
1002                                 FIXME("Only 8 Axes supported - ignoring extra\n");
1003                                 i = -1;
1004                             }
1005                             else
1006                                 axis++;
1007                         }
1008                         break;
1009                     }
1010                 }
1011 
1012                 if (i == sizeof(axis_names) / sizeof(axis_names[0]))
1013                 {
1014                     ERR("invalid joystick axis type: \"%s\"\n", ptr);
1015                     i = -1;
1016                 }
1017 
1018                 This->axis_map[tokens] = i;
1019                 tokens++;
1020             } while ((ptr = strtok(NULL, delim)) != NULL);
1021 
1022             if (tokens != This->device_axis_count)
1023             {
1024                 ERR("not all joystick axes mapped: %d axes(%d,%d), %d arguments\n",
1025                     This->device_axis_count, axis, pov, tokens);
1026                 while (tokens < This->device_axis_count)
1027                 {
1028                     This->axis_map[tokens] = -1;
1029                     tokens++;
1030                 }
1031             }
1032         }
1033     }
1034     else
1035     {
1036         int i;
1037 
1038         if (default_axis_map)
1039         {
1040             /* Use default mapping from the driver */
1041             for (i = 0; i < This->device_axis_count; i++)
1042             {
1043                 This->axis_map[i] = default_axis_map[i];
1044                 tokens = default_axis_map[i];
1045                 if (tokens < 0)
1046                     continue;
1047                 if (tokens < 8)
1048                     axis++;
1049                 else if (tokens < 15)
1050                 {
1051                     i++;
1052                     pov++;
1053                     This->axis_map[i] = default_axis_map[i];
1054                 }
1055             }
1056         }
1057         else
1058         {
1059             /* No config - set default mapping. */
1060             for (i = 0; i < This->device_axis_count; i++)
1061             {
1062                 if (i < 8)
1063                     This->axis_map[i] = axis++;
1064                 else if (i < 15)
1065                 {
1066                     This->axis_map[i++] = 8 + pov;
1067                     This->axis_map[i  ] = 8 + pov++;
1068                 }
1069                 else
1070                     This->axis_map[i] = -1;
1071             }
1072         }
1073     }
1074     This->devcaps.dwAxes = axis;
1075     This->devcaps.dwPOVs = pov;
1076 
1077     if (appkey) RegCloseKey(appkey);
1078     if (hkey)   RegCloseKey(hkey);
1079 
1080     return DI_OK;
1081 }
1082