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