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